.NET 4.0 中方法重载和协变的重大变化
从 .NET 3.5 迁移到 4.0 后,我遇到了一个奇怪的问题,一个函数导致 4.0 上的堆栈溢出,而在 3.5 框架上完美工作。我使用以下代码重现了该问题:
public interface IPerson
{
string Name { get; }
}
public class Person : IPerson
{
public Person() { }
public Person(IPerson source)
{
this.Name = source.Name;
}
public string Name { get; set; }
}
public class PersonList : List<Person>
{
public void AddRange(IEnumerable<IPerson> source)
{
this.AddRange(source.Select(p => new Person(p)));
}
}
引发错误:
IPerson otto = new Person { Name = "Otto" };
IPerson fritz = new Person { Name = "Fritz" };
PersonList list = new PersonList();
IEnumerable<IPerson> persons = new[] { otto, fritz };
list.AddRange(persons); //works on 3.5, stack overflow on 4.0
查看 PersonList
的 AddRange(IEnumerable
方法。 在 3.5 中,调用从 List
派生的方法 AddRange(IEnumerable
。在 4.0 中,尽管事实上带有参数的更好匹配函数 (IEnumerable
这种新行为是有意为之并记录在案的吗?
after migrating from .NET 3.5 to 4.0 i have a strange problem with a function resulting in stack overflow on 4.0 while working perfectly on the 3.5 framework. I reproduced the problem with the following code:
public interface IPerson
{
string Name { get; }
}
public class Person : IPerson
{
public Person() { }
public Person(IPerson source)
{
this.Name = source.Name;
}
public string Name { get; set; }
}
public class PersonList : List<Person>
{
public void AddRange(IEnumerable<IPerson> source)
{
this.AddRange(source.Select(p => new Person(p)));
}
}
Provoking the error:
IPerson otto = new Person { Name = "Otto" };
IPerson fritz = new Person { Name = "Fritz" };
PersonList list = new PersonList();
IEnumerable<IPerson> persons = new[] { otto, fritz };
list.AddRange(persons); //works on 3.5, stack overflow on 4.0
Have a look at the AddRange(IEnumerable<IPerson> source)
method of PersonList
.
In 3.5, the method AddRange(IEnumerable<Person> source)
is called, derived from List<Person>
. In 4.0 the AddRange(IEnumerable<IPerson> source)
method is called (recursion) due to covariance, in spite the fact that a better matching function with a parameter (IEnumerable<Person>I
) exactly matching the input parameter exists.
Is that new behaviour intended and documented?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是正确的 C# 行为,因为在 C# 中,如果更多派生类上的任何方法都是适用的候选者,它自动优于派生较少的类上的任何方法,即使派生较少的方法具有更好的签名匹配。 因此,C# 4 制作了
AddRange(IEnumerablesource)
一个适用的候选者,然后更好的签名AddRange(IEnumerablesource)
位于基类中,因此不会被选中。但由于规则的原因,它很容易在您的情况下修复。
This is proper C# behavior because in C# if any method on a more-derived class is an applicable candidate, it is automatically better than any method on a less-derived class, even if the less-derived method has a better signature match. So C# 4 has made
AddRange(IEnumerable<IPerson> source)
an applicable candidate and then the better signatureAddRange(IEnumerable<Person> source)
is in the base class so it doesn't get picked.But it's easily fixable in your case, because of the rule.