代码之家  ›  专栏  ›  技术社区  ›  Gary Brunton

.NET通用方法问题

  •  5
  • Gary Brunton  · 技术社区  · 17 年前

    我试图理解的概念。NET泛型,并在我自己的代码中实际使用它们,但我一直遇到问题。

    有人能试着向我解释一下为什么以下设置无法编译吗?

    public class ClassA
    {
        ClassB b = new ClassB();
    
        public void MethodA<T>(IRepo<T> repo) where T : ITypeEntity
        {
            b.MethodB(repo);
        }
    }
    
    public class ClassB
    {
        IRepo<ITypeEntity> repo;
    
        public void MethodB(IRepo<ITypeEntity> repo)
        {
            this.repo = repo;
        }
    }
    

    我得到以下错误:
    无法从IRepo转换<'T>IRepo<'ITypeEntity>

    MethodA被IRepo呼叫<'详细信息类型>对象参数,其中DetailType继承自ITypeEntity。

    我一直认为这应该编译,因为我将MethodA中的T约束为ITypeEntity类型。

    任何想法或反馈都会非常有帮助。

    谢谢。

    编辑: Nick R有一个很好的建议,但不幸的是,在我的背景下,我没有将ClassA通用化的选择。B类可能是。

    10 回复  |  直到 17 年前
        1
  •  3
  •   Nick Randell    17 年前

    使用泛型时,继承的工作方式不同。正如Smashery所指出的,即使TypeA继承自TypeB,myType<A类>不继承myType<B类>。

    因此,您不能调用定义为MethodA(myType<TypeB>b)的方法,期望得到myType>B类>并给它一个myType<A类>相反。所讨论的类型必须完全匹配。因此,以下内容无法编译:

    myType<TypeA> a; // This should be a myType<TypeB>, even if it contains only TypeA's
    
    public void MethodB(myType<TypeB> b){ /* do stuff */ }
    
    public void Main()
    {
      MethodB(a);
    }
    

    因此,在您的情况下,您需要通过IRepo<ITypeEntity>即使它只包含DetailTypes。你需要在两者之间进行一些转换。如果您使用的是通用IList,则可以执行以下操作:

    public void MethodA<T>(IList<T> list) where T : ITypeEntity
    {
      IList<T> myIList = new List<T>();
    
      foreach(T item in list)
      {
        myIList.Add(item);
      }
    
      b.MethodB(myIList);
    }
    

    我希望这有帮助。

        2
  •  3
  •   Lucas Wilson-Richter    17 年前

    好吧,这编译得很好。我基本上重新定义了类以接受泛型参数。这在你的背景下可能没问题。

    public interface IRepo<TRepo>
    {
    }
    
    public interface ITypeEntity
    {
    }
    
    
    public class ClassA<T> where T : ITypeEntity
    {
        ClassB<T> b = new ClassB<T>();
        public void MethodA(IRepo<T> repo)
        {
            b.MethodB(repo);
        }
    }
    public class ClassB<T> where T : ITypeEntity
    {
        IRepo<T> repo;
        public void MethodB(IRepo<T> repo)
        {
            this.repo = repo;
        }
    }
    
        3
  •  2
  •   JamesSugrue    17 年前

    这个问题很难理解。DetailType可以继承自ITypeEntity,但实际上不是ITypeEntity。您对DetailType的实现可能会引入不同的功能,因此DetailType实现了ITypeEntity,但不等于ITypeEntity。我希望这是有道理的。..

        4
  •  1
  •   Community CDub    8 年前

    请看 @monoxide 的问题

    as I said there ,查看Eric Lippert关于仿制药的方差和协方差的一系列帖子将使这一点更加清晰。

        5
  •  1
  •   Atif Aziz    17 年前

    我收到以下错误:无法 从IRepo转换<'T>向 IRepo<'ITypeEntity>

    您收到此编译错误是因为 IRepo<T> IRepo<ITypeEntity> 同样的事情。后者是前者的专业化。 IRepo<T> 是a 泛型类型定义 ,其中类型参数T是占位符,以及 IRepo<ITypeEntity> 是a 构造泛型 在泛型类型定义中,类型参数T from被指定为 ITypeEntity .

    我一直认为这应该 编译时,我将T约束在 方法A的类型为ITypeEntity。

    这个 where 约束在这里没有帮助,因为它只会限制您在呼叫站点为T提供的类型 MethodA .

    以下是MSDN文档中的术语(请参见 Generics in the .NET Framework )这可能会有所帮助:

    • 泛型类型定义是 类、结构或接口 声明作为 模板,带有占位符 它可以包含或使用的类型。 例如 Dictionary<<K, V> 类可以包含 两种类型:键和值。因为 它只是一个模板,你不能 创建类的实例, 结构或界面是 泛型类型定义。

    • 泛型类型参数或类型 参数,是中的占位符 泛型类型或方法定义。 这个 Dictionary<K, V> 泛型类型有两种类型 参数K和V 表示其密钥的类型,以及 价值观。

    • 构造的泛型类型,或 构造类型,是 为泛型指定类型 泛型类型的类型参数 定义。

    • 泛型类型参数可以是任何类型 它代替了通用 类型参数。

    • 通用术语泛型类型 包括构造类型和 泛型类型定义。

    • 约束是对 泛型类型参数。给 例如,您可以限制一个类型 实现的类型的参数 这个 IComparer<T> 通用的 接口,以确保实例 可以订购该类型的。你可以 还将类型参数约束为 具有特定基数的类型 类,具有默认值 构造函数,或引用 类型或值类型。用户 泛型类型不能替代类型 不满足的论点 约束。

        6
  •  0
  •   Nick Higgs    17 年前

    T是一个类型变量,在使用中会绑定到partular类型。该限制确保该类型将代表 子集 实现ITypeEntity的类型,不包括实现接口的其他类型。

        7
  •  0
  •   Mladen    17 年前

    在编译时,即使您对其进行了约束,编译器也只知道MethodA中的T是一个引用类型。它不知道它被限制为什么类型。

        8
  •  0
  •   Aaron Powell    17 年前

    这是对泛型的冗余使用,如果T只能是ITypeEntity的实例,那么就不应该使用泛型。

    泛型是当你有多个类型可以在某物内部时使用的。

        9
  •  0
  •   Smashery    17 年前

    如果B是a的子类,这并不意味着 Class<B> 是的一个子类 Class<A> 因此,出于同样的原因,如果你说“ T 是一个 ITypeEntity “,这并不意味着” IRepo<T> 是一个 IRepo<ITypeEntity> “。如果你想让它工作,你可能必须编写自己的转换方法。

        10
  •  0
  •   Joel Coehoorn    17 年前

    在围绕泛型方法展开讨论的背景下,请允许我给你一个简单的泛型函数。它是VB的IIf(Immediate if)的通用等价物,而IIf本身是对C风格三元运算符(?)的拙劣模仿。它对任何事情都没有用,因为真正的三元运算符更好,但也许它会帮助你理解泛型函数是如何构建的,以及在什么上下文中应该应用它们。

    T IIF<T>(bool Expression, T TruePart, T FalsePart)
    {
        return Expression ? TruePart : FalsePart;
    }