DOBON.NETプログラミング道掲示板

■34353 / 3階層)  ジェネリック型インターフェースを使った変数を扱いたい
□投稿者/ 魔界の仮面弁士 大御所(1247回)-(2019/11/01(Fri) 18:37:58)
  • アイコン2019/11/03(Sun) 15:28:57 編集(投稿者)

    No34352に返信(アフロさんの記事)
    > dynamicという宣言の仕方を知らなかったので、そんな方法があったかと驚きました。

    ジェネリックはコンパイル時点で型が確定していますが、
    dynamic の場合は実行時に動的に判定されます。

    そのため、ジェネリックに比べるとパフォーマンスは幾許か落ちますし、
    スペルミスや型の取り違いなどがあった場合も、
    コンパイル時点では検出されません。御注意ください。


    > しかし、インターフェースは反復処理で力を発揮するものと思っていましたが
    > ジェネリックにするとそうでもないのですかね…

    No34349 にあるような型定義だと使えませんが、ジェネリックの指定如何によっては
    共変性・反変性によって対処できることはありそうです。
    https://ufcpp.net/study/csharp/sp4_variance.html

    また、将来的には共変戻り値を使えるようになるかもしれません。
    ただし仮に実装されたとしても、.NET Framework では利用できず、
    .NET 5 あるいは .NET 6 以降になりそうです。
    https://ufcpp.net/blog/2019/10/pickuproslyn1004/



    > 今更な嘆きですが…
    今回の型パラメータ T が指し示す型は、
     interface IRoutine<T> : IRoutine where T : IRoutine
     {
      T Parent { get; set; }
     }
    と定義されています。

    なので、Parent プロパティが取り扱うインスタンスは、「少なくとも IRoutine 型を持つ」ことは間違いありません。

    その上で、実際のインスタンスについては、型定義が
     「class ClassA : IRoutine<ClassA>」
     「class ClassB : IRoutine<ClassB>」
    となっています。

    ClassA と ClassB は互いの継承関係を持ちません。
    両者の共通点は、「ジェネリックではない IRoutine インターフェイスを持つこと」だけです。

    この共通している IRoutine 型を使うことで、少なくとも
     『foreach (IRoutine routine in routines)』
    と書くことはできるでしょう。

    しかし IRoutine に Parent プロパティは無いので、
    このループ変数では、Parent に代入できません。

    型の確定した Parent を持つのは IRoutine ではなく、
    IRoutine<ClassA> や IRoutine<ClassB> 、
    あるいは ClassA や ClassB 型といった固有の型だけです。

    両者に共通した型が存在していない以上、このままでは
    ループ処理させることができないというわけです。
    (先述したように、dynamic やリフレクションを用いて、
    コンパイル時点ではなく実行時に解決させることならば可能)


    逆に言えば、これはつまり ClassA と ClassB の両方に対して
    「Parent プロパティを持つ共通の型」を継承もしくは実装しておくことで、
    foreach による取りまとめが可能になることを意味します。


    たとえば、こういう書き方をすることはできるでしょう。

    // 下記 2 つの定義はそのまま
    public interface IRoutine { /* 基本となる各種メンバー定義 */ }
    public interface IRoutine<T> : IRoutine where T : IRoutine
    {
      // Parent プロパティだけを定義
      T Parent { get; set; }
    }

    // 「Parent プロパティを持つ共通の型」を新たに定義
    abstract class RoutineBase : IRoutine<IRoutine>
    {
      // abstract または virtaual で定義
      public abstract IRoutine Parent { get; set; }
    }

    // 少なくとも「共通の型」を持つように実装する
    class ClassC : RoutineBase { … }
    class ClassD : RoutineBase, IRoutine<ClassD> { … }

    ***************

    var routines = new RoutineBase[] { new ClassC(), new ClassD() };
    foreach (RoutineBase routine in routines)
    {
      routine.Parent = IRoutine型の何か;
    }

    ===================

    上記では、「共通の型」として抽象クラスを設けていますが、
    もちろんインターフェイスのままでも OK です。


    // 「Parent プロパティを持つ共通の型」の定義
    interface IParent : IRoutine<IRoutine> { }


    // 少なくとも「共通の型」を持つように実装する
    class ClassE : IParent {
     public IRoutine Parent { get; set; }
    }
    class ClassF : IParent, IRoutine<ClassF> { … }

    ***************
    var routines = new IParent[] { new ClassE(), new ClassF() };
    foreach (IParent routine in routines)
    {
      routine.Parent = IRoutine型の何か;
    }


    あるいは、そもそも IRoutine<T> を廃止してしまい、上記 IParent を
     interface IParent : IRoutine
     {
       IRoutine Parent { get; set; }
     }
    にしてしまうという手もあります。
解決み!
違反を報告
削除キー/

前の記事(元になった記事) 次の記事(この記事の返信)
←Re[2]: ジェネリック型インターフェースを使った変数を扱いたい /アフロ 返信無し
 
上記関連ツリー

Nomalアイコン ジェネリック型インターフェースを使った変数を扱いたい / アフロ (19/11/01(Fri) 11:51) #34349
Nomalアイコン Re[1]: ジェネリック型インターフェースを使った変数を扱いたい / 魔界の仮面弁士 (19/11/01(Fri) 16:13) #34351
  └Nomalアイコン Re[2]: ジェネリック型インターフェースを使った変数を扱いたい / アフロ (19/11/01(Fri) 17:47) #34352 解決み!
    └Nomalアイコン ジェネリック型インターフェースを使った変数を扱いたい / 魔界の仮面弁士 (19/11/01(Fri) 18:37) #34353 解決み! ←Now

All 上記ツリーを一括表示 / 上記ツリーをトピック表示
 
上記の記事へ返信

Mode/  Pass/


- Child Tree -