5

我试图掌握 .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> 对象参数调用,其中 DetailType 继承自 ITypeEntity。

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

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

谢谢。

编辑: Nick R 有一个很好的建议,但不幸的是,在我的上下文中,我没有选择制作 ClassA Generic。B类可能是。

4

10 回答 10

3

好吧,这编译好了。我基本上重新定义了类以采用通用参数。这在您的上下文中可能没问题。

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;
    }
}
于 2008-09-23T22:52:22.383 回答
3

使用泛型时,继承的工作方式不同。正如 Smashery 指出的那样,即使 TypeA 继承自 TypeB,myType<TypeA> 也不会继承自 myType<TypeB>。

因此,您不能调用定义为 MethodA(myType<TypeB> b) 并期望 myType<TypeB> 的方法,而是给它一个 myType<TypeA> 。有问题的类型必须完全匹配。因此,以下内容将无法编译:

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> 传递给 MethodB,即使它仅包含 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);
}

我希望这是有帮助的。

于 2008-09-23T23:16:06.307 回答
2

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

于 2008-09-23T22:46:27.733 回答
1

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

你得到这个编译错误是因为IRepo<T>不是IRepo<ITypeEntity>一回事。后者是前者的专业化。是泛型类型定义,其中类型参数 T 是占位符,并且是泛型类型定义的构造泛型类型,其中类型参数 T from 被指定为。IRepo<T>IRepo<ITypeEntity>ITypeEntity

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

约束在where这里没有帮助,因为它只约束您可以在调用站点为 T 提供的类型MethodA

以下是 MSDN 文档中的术语(请参阅.NET Framework 中的泛型)可能会有所帮助:

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

  • 泛型类型参数或类型参数是泛型类型或方法定义中的占位符。Dictionary<K, V>泛型类型有两个类型参数 K 和 V,它们表示其键和值的类型。

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

  • 泛型类型参数是替代泛型类型参数的任何类型。

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

  • 约束是对泛型类型参数的限制。例如,您可以将类型参数限制为实现IComparer<T>泛型接口的类型,以确保可以对类型的实例进行排序。您还可以将类型参数约束为具有特定基类、具有默认构造函数或引用类型或值类型的类型。泛型类型的用户不能替换不满足约束的类型参数。

于 2008-09-23T22:53:00.417 回答
1

请参阅@monoxide的问题

正如我在那里所说的,查看 Eric Lippert 关于泛型的逆变和协变的系列文章将使这一点更加清晰。

于 2008-09-23T22:54:19.947 回答
0

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

于 2008-09-23T22:47:04.037 回答
0

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

于 2008-09-23T22:48:18.027 回答
0

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

于 2008-09-23T22:51:48.547 回答
0

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

泛型适用于当您有多种类型可以在某物内部时。

于 2008-09-23T22:52:32.103 回答
0

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

T IIF<T>(bool Expression, T TruePart, T FalsePart)
{
    return Expression ? TruePart : FalsePart;
}
于 2008-09-23T23:59:58.150 回答