Преодоление изменений в .NET 4.0 с перегрузкой методов и ковариацией - PullRequest
3 голосов
/ 05 апреля 2011

после перехода с .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

Посмотрите на метод AddRange(IEnumerable<IPerson> source) PersonList.В 3.5 вызывается метод AddRange(IEnumerable<Person> source), полученный из List<Person>.В 4.0 метод AddRange(IEnumerable<IPerson> source) вызывается (рекурсия) из-за ковариации, несмотря на то, что существует функция лучшего соответствия с параметром (IEnumerable<Person>I), точно совпадающим с входным параметром.

Это новое поведениезадумано и задокументировано?

1 Ответ

1 голос
/ 22 апреля 2011

Это правильное поведение C #, потому что в C #, если какой-либо метод в более производном классе является подходящим кандидатом, он автоматически лучше, чем любой метод в менее производном классе, даже если метод с меньшим производным имеет лучшее совпадение подписи. Таким образом, C # 4 сделал AddRange(IEnumerable<IPerson> source) подходящим кандидатом, и тогда лучшая подпись AddRange(IEnumerable<Person> source) находится в базовом классе, поэтому она не будет выбрана.

Но это легко исправить в вашем случае из-за правила.

public class PersonList  : List<Person>
{
    public void AddRange(IEnumerable<IPerson> source)
    {
           base.AddRange(source.Select(p => new Person(p)));
    }    
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...