- 投稿日:2021-10-25T18:58:29+09:00
ジェネリクスクラスをジェネリクス非依存で利用するには
はじめに 本記事はタイトル通り、ジェネリクスクラスをジェネリクス非依存で利用したいことがあったので、その時とった解決策をまとめることを目的としています。 もう少し具体的に言うと、ジェネリクスクラスをプロパティとして保持したいが、そのクラスはジェネリクスクラスにしたくないということが起きました。 色々な型を指定したジェネリクスクラスのインスタンスを保持できるようなクラスにしたいということです。 ジェネリクスクラスを保持するには、保持する側のクラスもジェネリクスでないとコンパイルエラーになります。しかし、これをやると、保持する側のクラスをインスタンス化した際に型引数に指定した型でしかジェネリクスクラスを扱えなくなってしまうため、色々な型を指定したジェネリクスクラスのインスタンスを保持できるクラスを作れません。 以下に、この時とった解決策を記載します。 解決策 ジェネリクスクラスのI/Fを作り、保持する側はI/Fで保持するようにします。 また、I/Fにはジェネリクスで定義したいプロパティやメソッドを扱える窓口を用意しておきます。 具体的には以下です。 // ジェネリクスクラスのI/Fです。 public interface ISample { // Action<T>型を実行するためのメソッドを用意しておきます。 // I/F側には、プロパティを用意せず、実行するメソッドだけを用意しておくのがポイントです。 // 引数の型は、ジェネリクスの型制約にするような基底クラスの型です。 // 今回はサンプルの所有者となるようなクラスがいくつかある前提で、その基底クラスを渡しています。 public void InvokeSampleAction(SampleOwner owner); // ... } // ジェネリクスクラスです。ISampleを実装しています。 public class Sample<T> : ISample where T : SampleOwner { // ジェネリクス型を引数に指定するAction型のプロパティ public Action<T> SampleAction { get; set; } // I/Fの実装で、自身が持っているSampleActionを実行する処理を書きます。 public void InvokeSampleAction(SampleOwner owner) { // このメソッドの引数は、ジェネリクスの型制約となるような基底クラスの型で渡ってきますが、 // 引数を、このインスタンスに指定されたT型にキャストし、それを用いてActionを実行することで、 // ジェネリクスに指定した型でジェネリクスではないI/FからAction<T>型を実行できるようにしています。 // (xxxにはジェネリクスで指定したものと同じ型のインスタンスが入ってくる前提です) if (!(owner is T t)) return; SampleAction.Invoke(t); } } 利用イメージは以下です。 public class SampleManager { // I/Fで保持します private IDictionary<string, ISample> m_Samples = new Dictionary<string, ISample>(); // 呼び出しはこんな感じです。 public void DoSomething(SampleOwner owner) { // ownerの情報から、呼び出したいSampleクラスをm_Samplesから抽出 var samples = ... foreach(var sample in samples) { sample.InvokeSampleAction(owner); } // ... } } さいごに インターフェースを使うことで、ジェネリクスクラスを横断的に扱えるようになるのは、面白いと思いました。 インターフェースはクラス間を疎結合に保つとかそういった使われ方をすることが多いと思いますが、こんな使い方もあるのは勉強になりました。 今後もこういった設計パターンを学んでいきたいと思います。
- 投稿日:2021-10-25T18:58:29+09:00
[C#]ジェネリッククラスをジェネリック非依存で利用するには
はじめに 本記事はタイトル通り、ジェネリッククラスをジェネリック非依存で利用したいことがあったので、その時とった解決策をまとめることを目的としています。 もう少し具体的に言うと、ジェネリッククラスをプロパティとして保持したいが、そのクラスはジェネリッククラスにしたくないということが起きました。 色々な型を指定したジェネリッククラスのインスタンスを保持できるようなクラスにしたいということです。 ジェネリッククラスを保持するには、保持する側のクラスもジェネリックでないとコンパイルエラーになります。しかし、これをやると、保持する側のクラスをインスタンス化した際に型引数に指定した型でしかジェネリッククラスを扱えなくなってしまうため、色々な型を指定したジェネリッククラスのインスタンスを保持できるクラスを作れません。 以下に、この時とった解決策を記載します。 解決策 ジェネリッククラスのI/Fを作り、保持する側はI/Fで保持するようにします。 また、I/Fにはジェネリックで定義したいプロパティやメソッドを扱える窓口を用意しておきます。 具体的には以下です。 // ジェネリッククラスのI/Fです。 public interface ISample { // Action<T>型を実行するためのメソッドを用意しておきます。 // I/F側には、プロパティを用意せず、実行するメソッドだけを用意しておくのがポイントです。 // 引数の型は、ジェネリックの型制約にするような基底クラスの型です。 // 今回はサンプルの所有者となるようなクラスがいくつかある前提で、その基底クラスを渡しています。 public void InvokeSampleAction(SampleOwner owner); // ... } // ジェネリッククラスです。ISampleを実装しています。 public class Sample<T> : ISample where T : SampleOwner { // ジェネリック型を引数に指定するAction型のプロパティ public Action<T> SampleAction { get; set; } // I/Fの実装で、自身が持っているSampleActionを実行する処理を書きます。 public void InvokeSampleAction(SampleOwner owner) { // このメソッドの引数は、ジェネリックの型制約となるような基底クラスの型で渡ってきますが、 // 引数を、このインスタンスに指定されたT型にキャストし、それを用いてActionを実行することで、 // ジェネリックに指定した型でジェネリックではないI/FからAction<T>型を実行できるようにしています。 // (xxxにはジェネリックで指定したものと同じ型のインスタンスが入ってくる前提です) if (!(owner is T t)) return; SampleAction.Invoke(t); } } 利用イメージは以下です。 public class SampleManager { // I/Fで保持します private IDictionary<string, ISample> m_Samples = new Dictionary<string, ISample>(); // 呼び出しはこんな感じです。 public void DoSomething(SampleOwner owner) { // ownerの情報から、呼び出したいSampleクラスをm_Samplesから抽出 var samples = ... foreach(var sample in samples) { sample.InvokeSampleAction(owner); } // ... } } さいごに インターフェースを使うことで、ジェネリッククラスを横断的に扱えるようになるのは、面白いと思いました。 インターフェースはクラス間を疎結合に保つとかそういった使われ方をすることが多いと思いますが、こんな使い方もあるのは勉強になりました。 今後もこういった設計パターンを学んでいきたいと思います。
- 投稿日:2021-10-25T14:40:57+09:00
C#でUnix Timeの取得
良くない例 DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds 良い例 new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds() 相違点 「良くない例」の型はdoubleで、「良い例」の型はlong。