2016-08-11 2 views
-1

Мы работаем над адаптацией некоторого стороннего кода путем расширения базового класса на производный класс, который мы можем настроить. Все работает отлично от одного метода, который возвращает IEnumerable.переопределить IEnumerable .NET

Мы создали код 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>; 
} 

EDIT: много хороших ответов, вкратце вопрос был ключевым словом yield. Поскольку это реализация сторонних разработчиков, я могу изменить код только в виртуальном методе. В конце концов, я закончил тем, что возвращал из него IEnumerable, а не возвращал из цикла.

Это заставило код работать в сторонней библиотеке.

+1

Поскольку это метод итератора, ваш код в методе фактически не вводится до начала перечисления, например. с 'foreach'. –

ответ

1

Это выражение:

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

... возвращает null без перечисления ничего, потому что тип выполнения итератора, возвращаемого методом не List<int> и не может быть приведен к List<int>.

x.GetList(...) возвращает объект итератора, который довольно дрожащего от нетерпения, чтобы сорвать через все эти yield return заявления, но тогда вы проверяете, чтобы увидеть, если это на самом деле List<int> вместо этого. Обнаружив, что это не List<int>, вы отбрасываете его в сторону.

Очевидно, это было не ваше намерение, но вот как работает оператор as в C#.

Если вы хотите получить List<T> от IEnumerable<T>, вы должны позвонить ему ToList(). Листинг ссылочных типов в C# не делает ничего особенного под обложками, если у вас нет conversion operator, и структура не предоставляет их в таких случаях. Я думаю, что это мудро. Я не поклонник конверсионных операторов. Они, как правило, являются одним из тех, кто помогает руками, которые срывают руку, когда вы меньше всего этого ожидаете, например, переопределяя Equals().

Как @Letseatlunch указывает, вы в идеале должны возвращаться IEnumerable<int>, а не IEnumerable, но так как это в override, я бы предположил, что это не вариант.Но это означает, что идти LINQy с ним, вы должны будете добавить Cast<int>() или что-то:

var listofints = x.GetList(typeof(decimal), "test").Cast<int>().ToList(); 
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(); 
} 

Обратите внимание, что GetList возвращается IEnumerable<int> теперь вместо IEnumerable поскольку IEnumerable имеет ToList.

+2

Просто, чтобы завершить свой ответ ... 'yield return' заставляет код не оцениваться до тех пор, пока не будет перечислен список (вызов' .ToList() ') – DanielS

1

Это происходит не из-за кастинга, но из-за yield предполагает итерации, и никто не заставляет любого рода итерации, поэтому ничего не происходит. Чтобы доказать это, просто переписать тот же код без литья, как этот

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

  • же неверный результат, как и раньше.

Чтобы правильно использовать функцию, как она была реализована, необходимо вызвать внутри цикл, например:

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

, или какой-то другой способ, как ToList() что силы сбор для перечисления.

Теперь Ваш метод фактически будет вызываться.

+0

Это не совсем так. Например, если его оригинальный Test2.GetList возвратил 'return new List ();' вместо того, чтобы использовать yield, тогда литье вызовет вызов метода и не совпадает с var y = x.GetList (typeof (decimal), «test»); – Letseatlunch

+0

@Letseatlunch: я хочу сказать, что проблема заключается не в том, что «кастинг» сам по себе, а из-за отсутствия вызова перечисления. мы можем достичь этого по-разному. – Tigran

+0

вы правы, что проблема не в том, что «кастинг» от _itself_, но проблема в том, что, поскольку он передан в список и возвращает null, он может быть перечислить _never_. Как вы показали, не делает ли он кастинг, он может использовать свой метод как есть. – Letseatlunch

Смежные вопросы