Почему операция Linq Cast <T> завершилась неудачно, когда я определил неявный листинг?

Я создал два класса, причем один из них имеет неявный бросок между ними:

public class Class1
{
    public int Test1;
}

public class Class2
{
    public int Test2;

    public static implicit operator Class1(Class2 item)
    {
        return new Class1{Test1 = item.Test2};
    }
}

Когда я создаю новый список одного типа и пытаюсь выполнить Cast <T> к другому, он выходит из строя с InvalidCastException:

List<Class2> items = new List<Class2>{new Class2{Test2 = 9}};
foreach (Class1 item in items.Cast<Class1>())
{
    Console.WriteLine(item.Test1);
}

Это, однако, прекрасно работает:

foreach (Class1 item in items)
{
    Console.WriteLine(item.Test1);
}

Почему неявный листинг, который не вызывается при использовании Cast <T> ?

Ответ 1

Потому что, глядя на код через Reflector, Cast не пытается принимать какие-либо неявные операторы приведения (код LINQ Cast сильно оптимизирован для особых случаев всех видов, но ничего в этом направлении) (так как многие языки .NET не будет).

Не вдаваясь в размышления и другие вещи, generics не предлагает какой-либо из ящиков способ во что бы то ни стало принять во внимание такие дополнительные вещи.

EDIT: В общем, более сложные объекты, такие как неявные/эксплицитные, операторы равенства и т.д. обычно не обрабатываются такими универсальными средствами, как LINQ.

Ответ 2

Вы также можете использовать это, чтобы делать кастинг с конверсиями при необходимости:

public static IEnumerable<TDest> CastAll<TItem, TDest>(this IEnumerable<TItem> items)
{
 var p = Expression.Parameter(typeof(TItem), "i");
 var c = Expression.Convert(p, typeof(TDest));
 var ex = Expression.Lambda<Func<TItem, TDest>>(c, p).Compile();

 foreach (var item in items)
 {
    yield return ex(item);
 }
}

Из http://adventuresdotnet.blogspot.com/2010/06/better-more-type-safe-alternative-to.html

Ответ 3

Спасибо за то, что я собирался где-то использовать этот точный случай. Ты спас мне кучу времени. В качестве возможного решения вашей проблемы вы можете использовать ConvertAll < > вместо этого, например:

foreach (Class1 item in items.ConvertAll<Class1>((i) => (Class1)i))
{
     Console.WriteLine(item.Test1);
}

EDIT: или если вы хотите быть более явным, что приведение неявно, это тоже работает:

foreach (Class1 item in items.ConvertAll<Class1>(i => i))
{
     Console.WriteLine(item.Test1);
}

Ответ 4

Решением может быть использование бит linq'ing здесь, если вам действительно нужен такой вид преобразования:

List items = new List{new Class2{Test2 = 9}};
foreach (Class1 item in (from x in items select (Class1)x))
{
    Console.WriteLine(item.Test1);
}