【问题标题】:generic inheritance in C#? [duplicate]C#中的泛型继承? [复制]
【发布时间】:2009-04-14 17:59:27
【问题描述】:

可能重复:
Why cannot C# generics derive from one of the generic type parameters like they can in C++ templates?

我可以的

public class MyGenericClass : DL
//but i cannot do
public class MyGenericClass <T> : T

我将如何做第二个?如果我不能这样做,我该怎么做?

public class MyGenericClass <T> 
{
    T obj;
    //have all MyGenericClass.XYZ call obj.XYZ
}

【问题讨论】:

  • 您尝试这样做的动机是什么?
  • 等待 C# 4.0 中的动态类型。

标签: c# generics inheritance


【解决方案1】:

这是不可能的,因为根据 T 的类型,MyGenericClass 的公共接口会改变。

如果您有许多不同的类都公开相同的接口,您可以声明 MyGenericClass 以公开该接口,并在所有函数的实现中将调用委托给 obj

【讨论】:

  • 但是在第二种情况下,T用于声明一个成员,该成员可以是公共的,然后MyGenericClass的公共接口会根据T的类型而改变。
  • 只是属性的数据类型,它本质上是由 .net 和泛型处理的。我的意思是,如果按照他想要的方式工作,整个函数和属性都将被添加和删除,而不是仅仅改变数据类型
  • 这是循环的:“固有地由泛型处理” - 为什么它不能固有地处理不起作用的情况? :)
  • 他们支持什么和不支持什么并不取决于我。虽然我会注意到其他几个答案表明了一些可能导致的问题。
  • 没问题 - 只是想您可能想改进您的答案。目前,它给出了错误的原因(“公共接口”)。
【解决方案2】:

具体问题,为什么不能这样做:

public class MyGenericClass<T> : T

你可以这样做:

public class MyGenericClass<T> 
{
   T obj;
}

原因是 CLR 希望能够为 MyGenericClass 编译单个版本的代码,该版本适用于为 T 指定的任何引用类型。

对于第二种情况,它可以这样做,因为它可以悄悄地将T 替换为object 并插入适当的强制转换,大致相当于:

public class MyGenericClass 
{
   object obj;
}

但是对于继承版本,这个技巧不起作用。

此外,许多有用的设施将无法通过接口约束来描述。当你从一个类型继承时,你可以做的不仅仅是调用它的方法——你也可以覆盖它们。考虑这个假设的例子:

class MyBase 
{
    public virtual void MyVirtual() { }
}

class MyGenericDerived<T> : T
{
    public override void MyVirtual() 
    {
        Console.WriteLine("Overridden!"); 
    }
} 

MyBase obj = new MyGenericDerived<MyBase>();
obj.MyVirtual();

我想要做的是类似于“混合”的东西,其中 MyGenericDerived 以应用到的任何基础为虚函数提供定义。但是编译器怎么知道 T 会有一个可以被覆盖的名为 MyVirtual 的方法呢?我需要对 T 施加约束。我将如何通过接口表达这一点?不可能。一旦允许从类型参数继承,使用接口来描述约束就不是一个合适的解决方案。所以这就是为什么它在今天的语言中不存在的另一个原因。

【讨论】:

    【解决方案3】:

    你可以这样做

    public interface IXyzable { void xyz(); }
    
    public class MyGenericClass<T> : IXyzable where T : IXyzable {
        T obj;
        public void xyz() {
            obj.xyz();
        }
    }
    

    编辑:现在我明白了这个问题

    【讨论】:

    • 能否请您提供一个工作代码来调用此类函数?
    • 我使用了以下代码 MyGenericClass myGenericClass = new MyGenericClass(); Console.WriteLine(myGenericClass.xyz()); Console.ReadLine();
    【解决方案4】:

    您将需要所有可能的 T 来实现某个接口,以便您知道 obj.XYZ() 有意义,然后您就可以这样做

    public interface Ixyz
    {
        void XYZ();
    }
    
    public class MyGenericClass<T> : Ixyz where T:Ixyz, new()
    {
        T obj;
    
        public MyGenericClass()
        {
            obj = new T();
        }
    
        public void XYZ()
        {
            obj.XYZ();
        }
    }
    

    我也让 MyGenericClass 实现了 Ixyz,因为它显然确实公开了正确的方法,但也许最好不要这样做,因为它允许

    var x = new MyGenericClass<MyGenericClass<SomeClass>>();
    

    这不太可能是个好主意。

    【讨论】:

      【解决方案5】:

      这几乎是鸭式打字,但您可以使用反射。 当您使用对 obj 的引用创建泛型类时,请使用反射来尝试找到具有正确签名的方法。只要存储方法的引用,性能就不会太差。

      class BaseGeneric<T>
      {
          private T obj;
          private MethodInfo mi;
          private const string MethodNameOfInterest = "Xyz";
      
          public BaseGeneric(T theObject)
          {
              this.obj = theObject;
              Type t = obj.GetType();
               mi = t.GetMethod(MethodNameOfInterest);
          }
      
          public void Xyz()
          {
              mi.Invoke(obj, null);
          }
      }   
      

      当然,您需要为错误检查等添加更多内容,但这是您可以做的要点。另外,不要忘记将 System.Reflection 命名空间添加到您的 using 子句中。

      【讨论】:

      • 为什么投反对票?我回答了这个问题,不是吗?我不建议这样做,但它确实解决了他的问题:BaseGeneric.Xyz() 调用底层对象的 Xyz() 方法,无论该类型是什么。
      • 您的解决方案具有反射的所有缺点,并且没有鸭式打字的好处。外部对象 (BaseGeneric) 必须具有在其上声明的所有函数。如果您知道这些函数,只需调用相同的函数即可?
      • 它缓存了MethodInfo,因此降低了性能损失。它看起来像底层对象 - 所以它嘎嘎作响。它确实让他重新实现每个方法,所以这是最大的缺点。但是,T 可以是任何实现 Xyz 的东西,这在某些情况下会派上用场。
      • 如果你让他手动实现方法,那为什么不直接调用实现方法呢?为什么要使用反射?那是反对票的来源
      • 是的,你是对的。我很欣赏这个解释。我的方法唯一的好处是 T 可以是任何具有方法 Xyz 的类。如果无法访问代码并且无法使其使用接口,这很方便。使用非常有限,但类似于 C# 4.0 中即将推出的动态类型。
      【解决方案6】:

      .NET 类型系统不允许您尝试的形式的类型声明。不允许这样做的一个原因应该是直观的:当T 是密封类(例如System.String)时,MyGenericClass&lt;T&gt; 将如何行动?

      如果您绝对需要此功能(并且您知道您将使用的类型 T 不是密封的),您可以在运行时使用 Reflection.Emit 命名空间中的类生成代理。使用 PostSharp 之类的 AOP 工具也可以实现这种效果。

      【讨论】:

      • MyGenericClass 会产生编译器错误。如果此语言功能可用,则从 T 继承将自动将 T 限制为未密封。虽然这只是问题的一部分(见我的回答)。
      【解决方案7】:

      这个呢:

      class BaseClass<T>
      {
          public T property { get; set; }
      }
      
      class GenericClass<T> : BaseClass<T>
      { 
      
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              GenericClass<int> l = new GenericClass<int>();
              l.property = 10;
          }
      }
      

      这样就实现了你想做的?

      【讨论】:

      • 我不这么认为。他希望 GenericClass 在不重新实现任何东西的情况下公开 T 的所有方法。至少,这是我对他在问什么的最好猜测。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-01
      • 2015-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多