Почему операция 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);
}