【问题标题】:C# generics - Can I make T be from one of two choices?C# 泛型 - 我可以让 T 来自两个选择之一吗?
【发布时间】:2010-06-21 15:17:18
【问题描述】:

假设我有以下类层次结构:

Class A {...}

Class B : A  {...}

Class C : A {...}

我目前拥有的是

Class D<T> where T : A {...}

但我想要某种形式的东西

Class D<T> where T in {B,C}

这是由于一些奇怪的行为,我不负责 B 和 C 具有不在 A 中的通用方法,但如果能够在 T 上的 D 中调用它们会很好。

注意:我无权访问 A、B 或 C 来编辑它们

【问题讨论】:

    标签: c# .net class generics class-design


    【解决方案1】:

    你需要为B和C中的常用方法定义一个接口(我们称之为Ibc),让B和C实现这个接口,然后你就可以编写了:

    Class D<T> where T : A, Ibc {...}
    

    【讨论】:

    • 但是,他不控制源 A、B 和 C。这会使这个解决方案有点难以实施。
    • 啊,我还没有看到编辑。如果这样的接口不存在,那么据我所知没有直接的方法来实现 D 类。
    • +1 - 这是首选解决方案。我添加了一个答案,说明如何在 some 情况下完成。很好奇你得到了接受的答案......@Jean-Bernard Pellerin - 你能解释一下你是如何解决它的吗?
    • 其实最后我没有使用你的任何一个答案。我向 A 添加了一个扩展方法,它被真实的覆盖了:)
    【解决方案2】:

    这是不可能的。

    正如其他人建议的那样,您可以定义一个接口并在BC 中实现它。

    如果这不是一个选项(例如,如果这些类超出您的控制范围),我可能会建议:首先,从一个抽象类开始,其中包含您可以通过任何T 派生实现的所有功能来自A。然后说你有一些方法同时存在于BC 中,它们不属于A。在D 中,您可以让这些抽象方法由子类实现:

    public abstract class D<T> where T : A
    {
        protected T _member;
    
        public void DoSomethingAllTsCanDo()
        {
            _member.DoSomething();
        }
    
        public abstract void DoSomethingOnlyBAndCCanDo();
    }
    

    然后您可以从BC 每种类型的基类继承并覆盖抽象方法以提供适当的功能:

    public class DB : D<B>
    {
        public override void DoSomethingOnlyBAndCCanDo()
        {
            _member.DoSomethingOnlyBCanDo();
        }
    }
    
    public class DC : D<C>
    {
        public override void DoSomethingOnlyBAndCCanDo()
        {
            _member.DoSomethingOnlyCCanDo();
        }
    }
    

    【讨论】:

      【解决方案3】:

      首先,如果 B 和 C 有共同的方法,这是他们不共享接口的设计缺陷。也就是说,即使无法访问 B 和 C,您也可以解决此问题。
      可以创建一个通用接口。假设你有:

      public class A
      {
      }
      public class B : A
      {
          public void Start() { }
      }
      public class C : A
      {
          public void Start() { }
      }
      

      您可以创建一个通用接口:

      public interface IStartable
      {
          void Start();
      }
      

      并在 B 和 C 的派生类上使用它:

      public class BetterB : B, IStartable
      {
      }
      public class BetterC : C, IStartable
      {
      }
      

      如果您按原样获取 B 和 C 实例,您可能无法实现,但如果您创建它们,则可以考虑。事实上,对于 B 和 C 的特殊类,您可以使用接口而不是 D&lt;T&gt;

      【讨论】:

      • 我喜欢这个答案。它还提供了一个更通用的解决方案,其中没有 A,例如如果 B 和 C 来自不同来源的库
      【解决方案4】:

      B 和 C 实现相同的接口吗?这可能是一条更好的路线。

      【讨论】:

        【解决方案5】:

        一些选项:

        1. 创建一个接口IderivedFromA,其中包含来自BC 的常用方法。
          从您的问题看来这是不可能的
        2. D 中将T 转换为dynamic 并动态调用方法
          最简单的解决方案,如果你可以使用.Net 4
        3. D 中测试您是否处理BC、强制转换和调用
          将由编译器检查,并且可以从.Net 2 中进行
        4. The Dan Tao answer:为BC创建一个D&lt;T&gt;的具体实现,可以直接调用BC的方法。 (我自己没想到这个)。
          只有在“用户源”知道它正在处理 BC 并且不使用抽象 A 时才有效使用D&lt;A&gt;。相反,它应该使用DBDC。但我认为是这样,否则你不需要泛型。

        【讨论】:

          【解决方案6】:

          C# 中的 where 约束不允许您指定多个类作为选择。 此外,如果您要指定多个 where contains,那么它们都必须满足。约束没有 OR 逻辑。 这是规范:http://msdn.microsoft.com/en-us/library/bb384067.aspx

          Grzenio 的回答似乎适合您。将通用行为提取到 B 和 C 的通用接口中。然后您可以将该接口用作约束。

          【讨论】:

            【解决方案7】:

            由于您无法访问源代码,唯一真正的答案(除非您愿意通过使用 dynamic 而失去安全) 明确检查 B/C和演员。

            【讨论】:

              猜你喜欢
              • 2011-08-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-06-09
              • 1970-01-01
              • 2011-10-20
              相关资源
              最近更新 更多