覆盖IEnumerable .NET

时间:2016-08-11 20:14:50

标签: c# .net

我们正在努力调整一些第三方代码,将基类扩展为我们可以调整的派生类。除了一个返回IEnumerable的方法之外,一切都运行良好。

我们已经创建了一些sudo代码来测试问题的确切位置,并且可以使用一个简单的示例重新创建它。

在该示例中,方法中的代码块下面永远不会被调用;我们已经在那里放置了断点,我们已经添加了一个可以立即抛出的异常。结果始终相同,调试器只是跳过该行。

有人可以解释我们做错了什么吗?第三方代码是否可能无法正常工作?

public class Test
{
    public virtual IEnumerable GetList(Type type, string key)
    {
        throw new NotImplementedException();
    }
}

public class Test2 : Test
{
    public override IEnumerable GetList(Type type, string key)
    {
        for (var x = 0; x <= 5; x++)
        {
            yield return x;
        }
    }
}

static void Main(string[] args)
{
        var x = new Test2();
        var y = x.GetList(typeof(decimal), "test") as List<int>;
}

编辑:很多好的答案,简而言之,问题是yield关键字。由于这是第三方实现,我只能更改虚拟方法中的代码。最后,我最终从它返回一个IEnumerable而不是从循环返回。

这使代码在第三方库中运行。

3 个答案:

答案 0 :(得分:6)

这是因为你要转向List<int>

var y = x.GetList(typeof(decimal), "test") as List<int>;

尝试:

public class Test
{
    public virtual IEnumerable<int> GetList(Type type, string key)
    {
       throw new NotImplementedException();
    }
}

public class Test2 : Test
{
    public override IEnumerable<int> GetList(Type type, string key)
    {
       for (var x = 0; x <= 5; x++)
       {
           yield return x;
       }
    }
}
static void Main(string[] args)
{
    var x = new Test2();
    var y = x.GetList(typeof(decimal), "test").ToList();
}

请注意,由于IEnumerable具有ToList,因此GetList现在返回IEnumerable<int>而不是IEnumerable

答案 1 :(得分:1)

这个表达式:

x.GetList(typeof(decimal), "test") as List<int>;

...返回null而不枚举任何内容,因为方法返回的迭代器的运行时类型不是List<int>,并且不能转换为List<int>

x.GetList(...)返回一个迭代器对象,它急切地想要翻阅所有yield return个语句,然后你会检查它是否真的是{{1}相反。发现它不是List<int>,你把它扔到一边。

显然,这不是你的意图,而是List<int>运算符在C#中的运作方式。

如果您需要as中的List<T>,则必须在其上调用IEnumerable<T>。 C#中的参考类型的演员除非你有conversion operator,否则不会做任何特别的事情,并且框架在这种情况下不提供。我认为这很明智。我不是转换运营商的粉丝。他们倾向于成为那些在你最不期望的时候撕开你的手的人之一,就像压倒ToList()一样。

正如@Letseatlunch所指出的那样,理想情况下你应该回归Equals()而不是IEnumerable<int>,但由于它是IEnumerable,我猜是这样的。不是一种选择。但这意味着要使用LINQy,你必须添加override或其他东西:

Cast<int>()

答案 2 :(得分:1)

由于强制转换,会发生这种情况,但由于yield意味着迭代,并且没有强制任何类型的迭代,所以没有任何反应。为了证明这一点,只需重新编写相同的代码而不进行强制转换,就像这样

var y = x.GetList(typeof(decimal), "test");

  • 同样不正确的结果,就像之前一样。

要正确使用您的功能,就像它实现的那样,您需要在里面调用 一个循环,例如:

foreach(var i in x.GetList(typeof(decimal), "test")) {}

,或使用强制收集枚举的ToList()等其他方法。

现在实际上会调用您的方法。