IEnumerable <T>. Один и литье - PullRequest
1 голос
/ 03 ноября 2011

У меня есть 2 объекта A и B. B унаследован от A и имеет еще несколько свойств. У меня есть IEnumerable {A}, который содержит только B объектов. Что я хочу сделать, это:

list.Single(b => b.PropertyThatOnlyExistOnB == "something")

Я бы ожидал, что что-то вроде этого будет работать:

list.Single((B) b => b.PropertyThatOnlyExistOnB == "something")

Но это не компилируется. Сейчас я просто делаю:

B result = null;
foreach (b in list)
{
     if((B)b.PropertyThatOnlyExistOnB == "something")
     {
      result = (B)b;
     }
}

Есть ли более короткий путь? Спасибо

Ответы [ 4 ]

9 голосов
/ 03 ноября 2011

Используйте методы расширения Enumerable.OfType<TResult> для фильтрации / приведения.

list.OfType<B>().Single(b => b.PropertyThatOnlyExistOnB == "something")
4 голосов
/ 03 ноября 2011

Хотя мне больше всего нравится ответ @ VirtualBlackFox, для полноты картины: Вот как можно воплотить свою идею в жизнь:

list.Single(b => ((B)b).PropertyThatOnlyExistOnB == "something");

Вы не так уж далеко отстали, за исключением того, что вы получили некоторые изСинтаксис перепутан.Синтаксис b => EXPRESSION обозначает лямбда-выражение.Вы не можете начать изменять материал до =>, если вы не хотите добавить (или удалить) аргументы:

* `x => LAMBDA_WITH_ONE_PARAMETER`
* `(x) => LAMBDA_WITH_ONE_PARAMETER`
* `() => LAMBDA_WITH_NO_PARAMETERS`
* `(x, y, z) => LAMBDA_WITH_THREE_PARAMETERS`
2 голосов
/ 03 ноября 2011

У меня есть IEnumerable<A>, который содержит только объекты B.

Я бы поставил под сомнение это утверждение относительно вашей переменной.Вы указали, что это IEnumerable<A>, но оно содержит только экземпляры B.Какова цель этого?Если вам явно требуются только экземпляры B при любых обстоятельствах, было бы лучше, чтобы это было IEnumerable<B>, поскольку оно защищает проблемы, которые могут быть обнаружены во время компиляции.

Примите во внимание следующее, я мог бы представить, что у вас может быть какой-то код, подобный следующему:

var setOfA = // Get a set of A.
DoSomethingWithA(setOfA);

var instanceOfB = GetInstanceOfB(setOfA);

В этом случае я могу понять, что IEnumerable<A> совершенно допустим, кроме случаев, когда выхотите выполнить последнюю операцию, GetInstanceOfB.Давайте представим, что определение таково:

B GetInstanceOfB(IEnumerable<A> setOfA)
{
    return // The answer to your question.
}

Итак, первоначальная проблема, которую, я надеюсь, вы видите, состоит в том, что вы помещаете все свои карты в представление о том, что ваш список (setOfA в моем примере),всегда будет содержать только экземпляры B.Хотя вы можете гарантировать, что с точки зрения разработчика компилятор не может сделать такое предположение, он может только гарантировать, что setOfA (список) является IEnumerable<A>, и в этом заключается потенциальная проблема.

Глядя на предоставленные ответы (все из которых являются совершенно действительными [@VirtualBlackFox является самым безопасным ответом] с учетом вашего представления):

У меня есть IEnumerable<A>, который содержит только B объектов.

Что если в некоторых будущих изменениях setOfA также содержит экземпляр C (потенциальный будущий подкласс A).С учетом этого ответа:

list.Single(b => ((B)b).PropertyThatOnlyExistOnB == "something");

Что, если setOfA на самом деле: [C B B].Вы можете видеть, что явное приведение (B)b вызовет выброс InvalidCastException.Из-за характера операции Single она будет продолжать перечислять до тех пор, пока в первом экземпляре не произойдет сбой предиката (PropertyThatOnlyExistOnB == "something") или не будет выдано исключение.В этом случае может быть сгенерировано исключение, которое является неожиданным и, вероятно, необработанным.Этот ответ похож на:

list.Cast<B>().Single(b => b.PropertyThatOnlyExistOnB == "something");

Учитывая этот ответ:

list.Single<A>(b => (b as B).PropertyThatOnlyExistOnB == "something")

В той же ситуации исключение может возникнуть как брошенный экземпляр NullReferenceException, так как экземпляриз C нельзя безопасно набрать B.

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

Принимая во внимание этот ответ:

list.OfType<B>.Single(b => b.PropertyThatOnlyExistOnB == "something");

Это позволяет безопасно вводить приведение к потенциальному подмножеству A это на самом деле B, и компилятор может гарантировать, что ваш предикат используется только на IEnumerable<B>.

Но это привело бы меня к обнаружению, что узел в вашем коде пытается обработать ваш IEnumerable<A>, но выполняет операцию, где вы действительно хотите свой IEnumerable<B>.В этом случае, не следует ли вам выполнить рефакторинг этого кода, чтобы возможно иметь явный метод:

B GetMatchingInstanceOfB(IEnumerable<B> setOfB)
{
    if (setOfB == null) throw new ArgumentNullException("setOfB");

    return setOfB.Single(b => b.PropertyThatOnlyExistOnB == "something");
}

Изменение в дизайне метода гарантирует, что он будет только явно принимать допустимый набор B,и вам не нужно беспокоиться о своем актерском составе в этом методе.Этот метод отвечает только за сопоставление одного элемента B.

Это, конечно, означает, что вам нужно вытолкнуть ваше заклинание на другой уровень, но это все еще гораздо более явно:

var b = GetMatchingInstanceOfB(setOfA.OfType<B>());

Я также предполагаю, что вы имеете достаточную обработку ошибок в ситуациях, когда предикат не будет работать, когда все экземпляры B, например, более 1 элемента удовлетворяет PropertyThatOnlyExistOnB == "something".

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

1 голос
/ 03 ноября 2011

Это должно работать нормально:

list.Single<A>(b => (b as B).PropertyThatOnlyExistOnB == "something")

Если вы не хотите рисковать исключениями, вы можете сделать это:

list.Single<A>(b => ((b is B)&&((b as B).PropertyThatOnlyExistOnB == "something")))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...