#c# #linq #implicit-conversion
#c# #linq #неявное преобразование
Вопрос:
На этой неделе я столкнулся с проблемой, касающейся неявных преобразований в C # для коллекций. Хотя это (использование implicit
) может быть не нашим окончательным подходом, я хотел, по крайней мере, доработать код, чтобы предложить команде в качестве опции. Я свел проблему к следующему примеру:
В моем примере у меня есть два класса: один, который представляет бизнес-объект (Foo), и один, который представляет клиентскую версию (View Object) этого бизнес-элемента (FooVO), как определено ниже…
public class Foo
{
public string Id {get; set;}
public string BusinessInfo {get; set;}
}
public class FooVO
{
public string Id {get; set;}
public static implicit operator FooVO( Foo foo )
{
return new FooVO { Id = foo.Id };
}
}
Моя проблема заключается в том, что у меня есть список объектов Foo, и я хочу преобразовать их в список объектов FooVO, используя мой неявный оператор.
List<Foo> foos = GetListOfBusinessFoos(); // Get business objects to convert
Я пытался
List<FooVO> fooVOs = foos; // ERROR
и
List<FooVO> fooVOs = (List<FooVO>) foos; // ERROR
и даже
List<FooVO> fooVOs = foos.Select( x => x ); // ERROR
Я знаю, что могу сделать это в цикле, но я надеялся на простой (LINQ?) способ преобразовать объекты за один снимок. Есть идеи?
Заранее благодарю вас.
Редактировать Исправлена опечатка в примере
Комментарии:
1. Ваше неявное преобразование в
FooVO
в этом примере фактически возвращаетFoo
, это намеренно?2. @Крис Уолш — Нет, это была опечатка. Я исправлю это. Спасибо, что поняли это.
Ответ №1:
Вопрос, очень похожий на этот, задается почти каждый день на SO. Вы не можете этого сделать, потому что это нарушает безопасность типов:
List<Giraffe> g = new List<Giraffe>();
List<Animal> a = g; // Should this be legal?
a.Add(new Tiger()); // Nope; we just added a tiger to a list of giraffes.
В C # 4.0 вы можете неявно конвертировать из IEnumerable<Giraffe>
в IEnumerable<Animal>
, потому что нет метода «Добавить», который мог бы все испортить. Но вы никогда не сможете выполнить подобное «ковариантное» преобразование, если преобразование типов элементов определяется пользователем. Это должно быть преобразование по ссылке или идентификатору.
Вам нужно будет создать второй список и копировать их по одному за раз. Или используйте вспомогательные методы LINQ, такие как Select и ToList, чтобы выполнить эту работу за вас.
Название концепции системы типов, которую вы хотите, — «ковариация»; ковариантная связь — это та, в которой вы рассуждаете «Жирафы — это животные, поэтому последовательности жирафов являются последовательностями животных». Если эта тема вас заинтересует, то вы можете прочитать о том, как мы добавили ковариацию (и контравариантность) в C # 4.0 здесь:
http://blogs.msdn.com/b/ericlippert/archive/tags/covariance and contravariance/default.aspx
Начните снизу.
Комментарии:
1. Отличный пример, Эрик. Спасибо.
2. OP никогда не говорил, что FooVO является подклассом Foo , поэтому a.Add(new Tiger()) не будет компилироваться. ОП спросил о 2 отдельных классах, которые нужно преобразовать друг в друга. Такая ситуация часто возникает при программировании на MVVM. Я извлекаю список <Customer> из своей базы данных, и мне нужно преобразовать в список<CustomerViewModel> для привязки данных к представлению.
3. // Нет; мы только что добавили тигра в список жирафов. Нет, вы добавили тигра в список животных, и это прекрасно. Я не понимаю, почему это могло бы нарушить безопасность типов.
4. @LHolleman: Значения ссылочных типов являются ссылками . Теперь это понятно?
5. Это не работает при возврате списка<Giraffe> из функции, которая имеет возвращаемый тип списка<Animal>
Ответ №2:
Причина, по которой ваши примеры не работают, заключается в том, что вы пытаетесь присвоить IEnumerable<FooVO>
a List<FooVO>
. Должно сработать следующее.
List<FooVO> fooVos = foos.Select<Foo,FooVO>(x => x).ToList();
Ответ №3:
List<FooVO> d = new List<FooVO>(foos.Select(x => (FooVO)x));
У меня работает.
Комментарии:
1. Ага — приведение внутри лямбда-выражения Select. Это работает и для меня. Спасибо.
2. Это был единственный способ заставить приведение работать из a . ToListAsync(). Спасибо, чувак!
Ответ №4:
Используйте метод ConvertAll, передаваемый в static implicit operator FooVO( Foo foo )
в качестве аргумента
List<FooVO> fooVOs = foos.ConvertAll<FooVO>(FooVO.FooVO);
Комментарии:
1. Я получаю ошибку компилятора с моим приведенным выше примером кода и вашим предложением. Однако, если я заменю (FooVO. FooVO) с (x => x) это будет работать. Я раньше не использовал ConvertAll<T>() — спасибо за предложение.