У меня есть 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"
.
Этовозможно, бессмысленно рассуждать о проверке вашего кода, но я думаю, что стоит подумать о непредвиденных ситуациях, которые могут возникнуть, и о том, как потенциальная настройка ваших переменных может спасти вас от потенциальной головной боли в будущем.