C# 中具有方差的通用方法?

发布于 2024-11-03 01:17:06 字数 502 浏览 3 评论 0原文

考虑以下类(继承树):

public class A {}
public class B: A {}

而这种方法:

public IList<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}

编译器不高兴。错误是无法转换表达式类型IList;返回类型 IList。我该如何解决这个问题?换句话说,如何指定 MyMethod 将返回 TIList,其中 T 可以是任何继承自 AA 本身的实例?

Consider following classes (inheritance tree):

public class A {}
public class B: A {}

And this method:

public IList<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}

The compiler is unhappy. Error is Cannot convert expression type IList<B> to return type IList<A>. How do I solve this ? In another words how to specify that MyMethod will return IList<T> of T where T can be anything that inherits from A or instances of A itself ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦言归人 2024-11-10 01:17:06

您所要求的是不可能的,因为 IList 不支持方差 - 您不能在任何需要 IList 的地方使用 IList ;。您必须解释更多您想要的细节才能找到解决方案。

可能的解决方案是:

public IList<A> MyMethod(){
    IList<A> result = new List<A>();

    //add some items to result

    return result;
}

或者

public IEnumerable<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}

What you're asking for is impossible because IList<T> does not support variance -- you cannot use IList<B> anywhere that is expecting IList<A>. You'll have to explain more details of what you want in order to come up with a solution.

Possible solutions are:

public IList<A> MyMethod(){
    IList<A> result = new List<A>();

    //add some items to result

    return result;
}

Or

public IEnumerable<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}
柒七 2024-11-10 01:17:06

您无法将 IList 转换为到 IList,即使 B 继承自 A。否则,用户可能会尝试将不是 B 的 A 实例添加到列表中。

public void Example(){
    IList<B> listB = new List<B>();
    IList<A> listA = listB;
    listA.Add(new A()); // Can't insert A into a list of B
}

你能返回 IEnumerable 吗?而不是 IList? IEnumerable与 IList 不同,它是协变的。

You cannot convert an IList<B> to IList<A>, even if B inherits from A. Otherwise, the user might attempt to add an instance of A that is not B into the list.

public void Example(){
    IList<B> listB = new List<B>();
    IList<A> listA = listB;
    listA.Add(new A()); // Can't insert A into a list of B
}

Can you return IEnumerable<A> instead of IList<A>? IEnumerable<A> is covariant, unlike IList<A>.

下壹個目標 2024-11-10 01:17:06

如何指定 MyMethod 将返回 T 的 IList,其中 T 可以是从 A 继承的任何内容或 A 本身的实例?


您不必这样做,

您只需声明它返回 IList 即可。为什么?因为 - 假设 B 继承自 A - B 的每一项都可以在所需类型为 A 的地方传递。

称之为继承多态性、里氏替换原理或方法方差,名称并不重要。重要的是以下内容有效(在 LinqPad 上测试):

public class A {}
public class B: A {}

public IList<A> MyMethod()
{
    var result = new List<A>();

    //add some items to result
    result.Add(new B());

    return result;
}

遗传替代方案

事实上,您可以知道您将返回一个 IList 并请求一些派生类型 (TB, TC...) 来填充它。是的,下面的示例也适用(在 LinqPad 上测试):

void Main()
{
    MyMethod<A, B, C>();
}

public class A {}
public class B: A {}
public class C: A {}

public IList<TA> MyMethod<TA, TB, TC>()
    where TB : TA, new()
    where TC : TA, new()
    where TA : class
{
    var result = new List<TA>();

    //add some items to result
    result.Add(new B() as TA);
    result.Add(new C() as TA);

    return result;
}

或者,如果您想保留特定的基类型(假设您想返回一个 IList 但它实际上包含以下类的项目)从 A 派生,那么你可以这样做:

void Main()
{
    MyMethod<B, C>();
}

public class A {}
public class B: A {}
public class C: A {}

public IList<A> MyMethod<T1, T2>()
    where T1 : A, new()
    where T2 : A, new()
{
    var result = new List<A>();

    //add some items to result
    result.Add(new T1() as A);
    result.Add(new T2() as A);

    return result;
}

你不必这样做,但是

如果你真的想说它返回 IList 其中T: A 然后说!

void Main()
{
    MyMethod<B>();
}

public class A {}
public class B: A {}
//public class C: A {} //Even if I don't add that class

public IList<T> MyMethod<T>()
    where T : A, new()
{
    var result = new List<T>();

    //add some items to result
    result.Add(new T());

    return result;
}

是的,那个不能返回 T 类型的项目和 A 类型的项目,因为它说它返回 >IList 并且并非 A 类型的每个项目也是 T 类型的项目


您的代码会发生什么情况

查看您的代码:

public IList<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}

您是尝试返回 IList当您说您要返回一个 IList 时,我们假设这有效...那么您的方法的调用者会发生什么?让我们看看:

public class A {}
public class B: A {}
public class C: A {}

void Main()
{
    //Hmmm... I need a IList<T>, let's call MyMethod!
    IList<A> list = MyMethod();
    //Cool, I got an IList<A>, now let's add some items...
    var item = new C();
    //Well, item is of type C...
    // and C inherits from A, so I must be able to add it...
    list.Add(item); //BOOM!
    //It was actually an IList<B>!
    // and C doesn't dervive from B, so you can't add it.
}

DFTBA!

how to specify that MyMethod will return IList of T where T can be anything that inherits from A or instances of A itself ?


You don't have to

You can just declare that it returns IList<A>. Why? Becase - given that B inherits from A - every item of B can be passed where the requiered type is A.

Call it polymorphism by inheritance, Liskov substitution principle, or method variance, the name doesn't matter. What matters is that the following works (tested on LinqPad):

public class A {}
public class B: A {}

public IList<A> MyMethod()
{
    var result = new List<A>();

    //add some items to result
    result.Add(new B());

    return result;
}

Genetic alternatives

In fact, you can tell that you are going to return a IList<TA> and request a few derived types (TB, TC...) to populate it with. That's right, the following example also works (tested on LinqPad):

void Main()
{
    MyMethod<A, B, C>();
}

public class A {}
public class B: A {}
public class C: A {}

public IList<TA> MyMethod<TA, TB, TC>()
    where TB : TA, new()
    where TC : TA, new()
    where TA : class
{
    var result = new List<TA>();

    //add some items to result
    result.Add(new B() as TA);
    result.Add(new C() as TA);

    return result;
}

Or if you want to keep a particular base type (say you want to return an IList<A> but it actually contains items of classes that derive from A, then you can do this:

void Main()
{
    MyMethod<B, C>();
}

public class A {}
public class B: A {}
public class C: A {}

public IList<A> MyMethod<T1, T2>()
    where T1 : A, new()
    where T2 : A, new()
{
    var result = new List<A>();

    //add some items to result
    result.Add(new T1() as A);
    result.Add(new T2() as A);

    return result;
}

You don't have to, but you can

OK, if you really want to say it returns IList<T> where T : A. Then say that!

void Main()
{
    MyMethod<B>();
}

public class A {}
public class B: A {}
//public class C: A {} //Even if I don't add that class

public IList<T> MyMethod<T>()
    where T : A, new()
{
    var result = new List<T>();

    //add some items to result
    result.Add(new T());

    return result;
}

Yes, that one cannot return a mix of item of type T and items of type A, because it says it returns IList<T> and not every item of type A is also an item of type T.


What happens with your code

Look at your code:

public IList<A> MyMethod(){
    IList<B> result = new List<B>();

    //add some items to result

    return result;
}

You are trying to return an IList<B> when you said that you was going to return an IList<A>. Let's suppose that that works... then what would happen to the caller of your method? Let's see:

public class A {}
public class B: A {}
public class C: A {}

void Main()
{
    //Hmmm... I need a IList<T>, let's call MyMethod!
    IList<A> list = MyMethod();
    //Cool, I got an IList<A>, now let's add some items...
    var item = new C();
    //Well, item is of type C...
    // and C inherits from A, so I must be able to add it...
    list.Add(item); //BOOM!
    //It was actually an IList<B>!
    // and C doesn't dervive from B, so you can't add it.
}

DFTBA!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文