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?开发者_如何学JAVA
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 signature AddRange(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.
public class PersonList : List<Person>
{
public void AddRange(IEnumerable<IPerson> source)
{
base.AddRange(source.Select(p => new Person(p)));
}
}
精彩评论