简体   繁体   English

为什么我不能将具体类型列表分配给该具体界面的列表?

[英]Why can I not assign a List of concrete types to a List of that concrete's interface?

Why does this not compile? 为什么这不编译?

public interface IConcrete { }

public class Concrete : IConcrete { }

public class Runner
{
    public static void Main()
    {
        var myList = new List<Concrete>();
        DoStuffWithInterfaceList(myList);  // compiler doesn't allow this
    }

    public static void DoStuffWithInterfaceList(List<IConcrete> listOfInterfaces) { }

}

And what's the quickest way to get myList to the correct type? 什么是将myList设置为正确类型的最快方法?

EDIT I messed up the DoStuffWithInterfaceList example 编辑我弄乱了DoStuffWithInterfaceList示例

Almost all these answers say that this will be supported in C# 4. They are all wrong. 几乎所有这些答案都说这将在C#4中得到支持。他们都错了。

Just to be crystal clear: this is not an example of covariance that we will be supporting in C# 4, because doing so would not be typesafe. 简单来说: 这不是我们将在C#4中支持的协方差的一个例子,因为这样做不会是类型安全的。 We are supporting typesafe covariance and contravariance of generic interfaces and delegates which are constructed with reference type arguments . 我们支持使用引用类型参数构造的泛型接口和委托的类型安全协方差和逆变 The example here uses a class type, List, not an interface type. 这里的示例使用类类型List,而不是接口类型。 And the interface type, IList, is not typesafe for covariance or contravariance. 并且接口类型IList对于协方差或逆变不是类型安全的。

IEnumerable will be covariant, as it is an interface that is safe for covariance. IEnumerable将是协变的,因为它是一个对协方差安全的接口。

The accepted solution is quite inefficient for large lists, and completely unnecessary. 对于大型列表,接受的解决方案效率非常低,而且完全没有必要。 You can change the signature of your method ever so slightly to make the code work without any conversions, either implicit or explicit: 您可以稍微更改方法的签名,以使代码无需任何隐式或显式转换即可运行:

public class Runner
{
    public static void Main()
    {
        var myList = new List<Concrete>();
        DoStuffWithInterfaceList(myList);  // compiler doesn't allow this
    }

    public static void DoStuffWithInterfaceList<T>(List<T> listOfInterfaces)
        where T: IConcrete
    { }
}

Notice that the method is now generic and uses a type constraint to ensure that it can only be called with lists of IConcrete subtypes. 请注意,该方法现在是通用的,并使用类型约束来确保只能使用IConcrete子类型列表调用它。

Currently, this is forbidden because otherwise the type safety would be broken. 目前,这是禁止的,否则类型安全将被打破。 you could do something like this inside of DoStuffWithInterfaceList: 你可以在DoStuffWithInterfaceList中做这样的事情:

public class OtherConcrete : IConcrete { }

public void DoStuffWithInterfaceList(List<IConcrete> listOfInterfaces) 
{
       listOfInterfaces.Add(new OtherConcrete ());
}

Which will fail at run time because listOfInterfaces is of type Concrete only. 哪个会在运行时失败,因为listOfInterfaces只是Concrete类型。

As others said, this will be possible is C# 4 as long as you don't change the list inside the method but you'll have to explicitly tell it to the compiler. 正如其他人所说,只要您不更改方法内的列表,就可以使用C#4,但是您必须明确告诉编译器。

To answer your other question about converting the list, If you're using .Net 3.5 I would go with Enumerable.Cast<> extension method. 要回答有关转换列表的其他问题,如果您使用的是.Net 3.5,我将使用Enumerable.Cast <>扩展方法。 otherwise, you can write a lazy conversion method yourself using the yield keyword, which will give you the same effect. 否则,你可以使用yield关键字自己编写一个惰性转换方法,这将给你相同的效果。

EDIT: 编辑:

As Eric Lippert said, you should use IEnumerable in order for it to work in C# 4. 正如Eric Lippert所说,你应该使用IEnumerable来使它在C#4中工作。

C# does not currently support converting generic types like that ( it will be supported in C# 4, if I understand it correctly As wcoenen states in comments below, and Eric also clarifies in his answer, the only way to make it work in C#4 is to use IEnumerable<IConcrete> ). C#目前不支持转换类似的泛型类型( 如果我理解正确的话,它将在C#4中得到支持 正如wcoenen在下面的评论中所述,并且Eric在他的回答中也澄清了,使它在C#4中工作的唯一方法是使用IEnumerable<IConcrete> For now you will need to convert your list in some way. 现在,您需要以某种方式转换列表。

You could call the method like this: 您可以像这样调用方法:

DoStuffWithInterface(myList.ConvertAll<IConcrete>(n => n as IConcrete));

Update 更新
I realized that you probably don't need the cast inside the lambda, even though I sort of like it for clarity. 我意识到你可能不需要在lambda中使用cast,尽管为了清晰起见我有点喜欢它。 So this should also work: 所以这也应该有效:

DoStuffWithInterface(myList.ConvertAll<IConcrete>(n => n));

This has to do with covariance and contravariance. 这与协方差和逆变有关。 Eric Lippert wrote a lot about it earlier this year. Eric Lippert在今年早些时候写了很多关于它的文章。 (11 blog entries specifically on that topic.) The first one is Covariance and Contravariance in C#, Part One . (11篇专门针对该主题的博客文章。)第一篇是C#中的协方差和逆变,第一部分 Read that and search his blog for the rest of them. 阅读并阅读他的博客,了解其余部分。 He provides detailed explanations of why this kind of thing is difficult. 他详细解释了为什么这种事情很困难。

Good news: some of the restrictions are lifted in C# 4.0. 好消息:C#4.0中解除了一些限制。

IList wouldn't work because IList isn't contravariant. IList不起作用,因为IList不是逆变的。 It needs to be IEnumerable though again this only works in 4.0. 它需要是IEnumerable,但这只适用于4.0。 You could also just use a ConvertAll with a lambda expression and that will work in 3.5 您也可以使用带有lambda表达式的ConvertAll,它将在3.5中工作

You could try 你可以试试

public void DoStuffWithInterface(IList<IConcrete> concrete) { }

but I think this only works in .NET 4.0. 但我认为这只适用于.NET 4.0。

If you wanna be dirty just do 如果你想变脏,就去做吧

public void DoStuffWithInterface(IList concrete) { }

and check to see if the objects coming out are concrete. 并检查出来的物体是否具体。

foreach (var item in myList)
    DoStuffWithInterface(item);

or 要么

public void DoStuffWithInterface(IList<IConcrete> concrete) { }

or 要么

var myNewList = myList.Cast<IConcrete>();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我不能将具体类添加到具体类实现的接口列表中? - Why can't I add concrete classes to a list of interfaces which the concrete classes implement? 如何转换列表<interface>列出<concrete> ? - How do I convert a List<interface> to List<concrete>? 获取具体 class 实现的特定接口的类型列表 - Get list of type(s) of specific interface implemented by concrete class 为什么我不能将此接口转换为具体的类? - Why can't I cast this interface to a concrete class? 为什么不能将强制转换接口键入其具体实现? - Why can I not type cast Interface into its concrete implementation? 即使所有具体类型现在都引用新版本的接口,我也可以将对象反序列化回原始接口 - Can i deserialize object back to original interface even if all the concrete types now reference new version of interface 由于具体类和接口没有IEnumerable的匹配返回类型而导致错误 <T> 和列表 <T> - Error due to concrete class and interface not having matching return types for IEnumerable<T> and List<T> 是否可以避免将接口方法参数的类型转换转换为同一类家族的具体类型? - Can I avoid the typecasting of interface method parameters to concrete types for same family of classes? 为什么我不能用返回具体类型的方法实现返回接口的接口方法 - Why can't I implement an interface method that returns an interface with a method that returns a concrete type 声明具体实现具有具体类型的接口 - Declare an interface where concrete implementation has concrete types
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM