开发者

Breaking change in .NET 4.0 with method overloading and covariance

开发者 https://www.devze.com 2023-02-22 17:27 出处:网络
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 followi

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)));
    }    
}
0

精彩评论

暂无评论...
验证码 换一张
取 消