#c# #foreach #ienumerable #linq-to-objects #generic-list
#c# #foreach #ienumerable #ссылка на объекты #общий список
Вопрос:
К моему личному стилю кодирования относится Enumerable.OfType<T>()
. Я использую это везде, где в этом есть хоть какой-то смысл. Особенно IEnumerable<T>
обеспечивает мощную функциональность linqToObject. Я ненавижу «небезопасный для типа» цикл ObjectCollections, такой как примеры ниже. Теперь у меня есть несколько вопросов о циклическом просмотре этих «неродственных коллекций».
ConfigurationSectionCollection
HttpModuleCollection
SPBaseCollection
SPFieldCollection
ArrayList
- и так далее…
Вопрос 1: Если я преобразую это ArrayList
в Enumerable<T>
, насколько велик дополнительный цикл по сравнению с простыми проверками foreach / if?
var ark = new ArrayList();
ark.Add(new Human());
ark.Add(new Human());
ark.Add(new Animal());
Вместо:
foreach (object passenger in ark)
{
if (passanger is Human) { }
if (passanger is Animal) { }
}
Я использую:
foreach (var human in ark.OfType<Human>())
{
}
foreach (var animal in ark.OfType<Animal>())
{
}
Вопрос 2: Какой способ приведения / преобразования будет использоваться во время цикла foreach в другую типизированную переменную? Это языковая особенность или это работает из коробки?
foreach (Human human in ark) { }
Спасибо, что терпите мой ужасный английский. С наилучшими пожеланиями, Бенджамин
Комментарии:
1. Примечание редактора: ужасный английский был исправлен.
Ответ №1:
Ans 1:
В вашем примере — возможно, вы фактически дважды повторяете ПОЛНОЕ перечисляемое.
// i use
foreach (var human in ark.OfType<Human>())
{
}
foreach (var animal in ark.OfType<Animal>())
{
}
Ans 2:
Это вызвало бы исключение InvalidCastException, если в ark есть какие-либо нечеловеческие объекты.
Я бы лично предпочел ark.OfTypes<T>()
, на случай, если я знаю, что хочу иметь дело только с Human
и Animals
, но буду игнорировать Elves
. Таким образом, код становится намного чище, и вы имеете дело со строго типизированным объектом в вашем цикле foreach.
Но опять же, на случай, если я не хочу игнорировать Elves
, я бы предпочел выполнить итерацию по полному списку массивов и использовать приведения.
Комментарии:
1. Список массивов в общий IEnumerable < T>
2. @ben — теперь, после твоей правки, это имеет смысл. Я обновлю ответ.
Ответ №2:
Вы не найдете никакой разницы, поскольку ArrayList реализует IEnumerable. На самом деле, все, что реализует этот интерфейс, может быть использовано в инструкции foreach .
Приведение объекта вместо настройки var приведет к сборке (поскольку это явное приведение), но если у вас есть animal внутри перечисления, вам в конечном итоге придется иметь дело с исключениями приведения.
foreach (Animal animal in ark) { } // this will blow up an exception if the object is Human
Ответ №3:
Чтобы предотвратить повторение коллекции два раза и сохранить какой-то понятный синтаксис, я бы предложил метод расширения, который позволяет связывать запросы foreach для указанных типов. Я подумал, что это было бы забавно:
static void Main(string[] args)
{
var ugly = new ArrayList();
ugly.Add("strings are evil");
ugly.Add("yeah");
ugly.Add(1);
ugly.Add(3);
ugly.Add(1234);
ugly.WithType<int>(x => Console.WriteLine("im a lousy int! " x))
.AndType<string>(x => Console.WriteLine("and im dangerous string: " x))
.Execute();
Console.ReadKey();
}
static TypedCollectionView WithType<T>(this IEnumerable x, Action<T> action)
{
return new TypedCollectionView(x).AndType(action);
}
class TypedCollectionView
{
private readonly IEnumerable collection;
private readonly Dictionary<Type, Action<object>> actions = new Dictionary<Type, Action<object>>();
public TypedCollectionView(IEnumerable collection)
{
this.collection = collection;
}
public TypedCollectionView AndType<T>(Action<T> action)
{
actions.Add(typeof(T), o => action((T)o));
return this;
}
public void Execute()
{
foreach (var item in collection)
{
var itemType = item.GetType();
foreach (var action in actions.Where(kv.Key.IsAssignableFrom(itemType)).Select(kv.Value))
{
action(item);
}
}
}
}
итак .. теперь, как сделать это менее отстойным? 😀
Ответ №4:
Enumerable.OfType
пройдутся по всем элементам и выполнят is T
проверку за вас. Таким образом, каждый OfType
вызов будет выполнять итерацию по всем элементам в коллекции. Это должно быть медленнее. Но другое дело, если это медленнее до такой степени, что вы не должны этого делать в своей программе.
Я бы подумал, есть ли смысл помещать объекты классов, у которых нет одного и того же родителя, в одну коллекцию. Может быть, вы могли бы найти какую-нибудь абстракцию, а затем в foreach привести все объекты к базовому классу и использовать полиморфные вызовы.
Комментарии:
1. этот пример arraylist — не мой код. дженерики с ковариантом намного современнее. я смогу лучше обращаться с «ObjectCollections» в старых частях .NET Framework.