简体   繁体   English

尝试转换IEnumerable时出现异常?

[英]Exception when attempting to cast IEnumerable?

I'm still in my first couple weeks of learning C# for a project, and I'm trying to properly implement the IEnumerable interface. 我还在学习C#项目的前几周,我正在尝试正确实现IEnumerable接口。 I've read a number of tutorials/guidelines, but I still seem to be doing something incorrectly. 我已经阅读了许多教程/指南,但我似乎仍然做错了什么。 I have a strong Java background, so I think some of my knowledge of Java generics is muddling my understanding how they work in C#. 我有一个强大的Java背景,所以我认为我对Java泛型的一些知识让我理解它们如何在C#中工作。

A class that I cannot alter contains an instance variable: 我无法改变的类包含一个实例变量:

public IEnumerable<object> Items;

And I'd like to provide it with an instance of my SampleDataSource class. 我想为它提供一个SampleDataSource类的实例。 This class acts as a storage container for a List of MyObject types: 此类充当List of MyObject类型的存储容器:

    public class SampleDataSource : IEnumerable
    {
        public List<MyObject> Subjects { get; private set; }

        public IEnumerator<MyObject> GetEnumerator()
        {
            return this.Cast<MyObject>().GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

But when I attempt to cast an instance of this SampleDataSource class to an IEnumerable<object> using this code: 但是当我尝试使用此代码将此SampleDataSource的实例 SampleDataSource转换为IEnumerable<object>

Items = (IEnumerable<MyObject>)App.SampleData;

I receive an exception: 我收到一个例外:

Additional information: Unable to cast object of type 'Expression.Blend.SampleData.SampleDataSource.SampleDataSource' to type 'System.Collections.Generic.IEnumerable`1[Expression.Blend.SampleData.SampleDataSource.MyObject]'. 附加信息:无法将类型为“Expression.Blend.SampleData.SampleDataSource.SampleDataSource”的对象强制转换为'System.Collections.Generic.IEnumerable`1 [Expression.Blend.SampleData.SampleDataSource.MyObject]'。

But I don't quite understand why - isn't my SampleDataSource properly acting as an IEnumerable that returns an enumerator containing MyObject types? 但我不太明白为什么 - 我的SampleDataSource是否正确地作为IEnumerable正在返回包含MyObject类型的枚举器?

It can't just implement the methods, but rather has to actually implement the interface. 它不仅可以实现方法,而且必须实际实现接口。

In your case, it'd be better to just make your class implement the appropriate interface. 在你的情况下,最好让你的类实现适当的接口。

public class SampleDataSource : IEnumerable<MyObject>, IEnumerable

Otherwise, you can use something like linfu to duck-type your class to the interface. 否则,你可以使用像linfu这样的东西将你的类打包到接口。

The other potential object is to use OfType<T> to handle this: 另一个潜在的对象是使用OfType<T>来处理这个:

// returns enumerable of the items that are actually assignable to MyObject
IEnumerable<MyObject> values = theDataSource.OfType<MyObject>();

I realize the question is answered already, but I missed a simple explanation of the problem behind your question, so here goes: 我已经意识到这个问题已经回答了,但是我错过了对你问题背后问题的一个简单解释,所以这里有:

The reason you cannot assign your SampleDataSource class to an IEnumerable<object> field is that SampleDataSource does not implement IEnumerable<object> . 您无法将SampleDataSource类分配给IEnumerable<object>字段的原因是SampleDataSource不实现IEnumerable<object> While it does implement IEnumerable , that's not good enough, because IEnumerable<object> is a subtype of IEnumerable . 虽然它确实实现了IEnumerable ,但这还不够好,因为IEnumerable<object>IEnumerable子类型

Because of interface covariance, you will be able to assign the class to the IEnumerable<object> field if it implements IEnumerable<MyObject> , provided that MyObject is in fact a reference type (ie, a class). 由于接口协方差,如果它实现IEnumerable<MyObject> ,您能够将类分配给IEnumerable<object>字段,前提是MyObject实际上是引用类型(即类)。 That's only applicable to C# 4.0 and later, however. 但是,这仅适用于C#4.0及更高版本。

Reed Copsey's answer doesn't mention that you could declare the SampleDataSource class thus, because IEnumerable<T> inherits from IEnumerable : Reed Copsey的回答没有提到你可以声明SampleDataSource类,因为IEnumerable<T>继承自IEnumerable

public class SampleDataSource : IEnumerable<MyObject>

Further, since you're just wrapping the contained collection, you would normally implement it thus: 此外,由于您只是包装所包含的集合,因此通常会实现它:

public class SampleDataSource : IEnumerable<MyObject>
{  
    public List<MyObject> Subjects { get; private set; }  

    public IEnumerator<MyObject> GetEnumerator()  
    {  
        return Subjects.GetEnumerator();
    }  

    IEnumerator IEnumerable.GetEnumerator()  
    {  
        return GetEnumerator();  
    }  
}  

This is questionable from an object-orientation perspective, though, since you're giving the public read-write access to the list. 但是,从面向对象的角度来看,这是有问题的,因为您正在为列表提供公共读写访问权限。 (The fact that the setter is private means that the public can't reassign the list reference, but it doesn't prevent them from calling Add or Remove or Clear on the list.) (setter是私有的这一事实意味着公众不能重新分配列表引用,但它不会阻止它们在列表上调用AddRemoveClear 。)

Another take on the object orientation question would be to ask: if a SampleDataSource contains a sequence of MyObjects, why would it also be a sequence of MyObjects? 另取在面向对象的问题是要问:如果一个SampleDataSource 包含 MyObjects序列,为什么会也是 MyObjects的序列? Instead of doing this: 而不是这样做:

classICannotAlter.Items = (IEnumerable<object>)someSampleDataSourceInstance;

maybe you should be doing this: 也许你应该这样做:

classICannotAlter.Items = (IEnumerable<object>)someSampleDataSourceInstance.Subjects;

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM