Как отрефакторить код с приведением типов иерархии классов c проверкой каждого типа через element.GetType == typeof(Class)?
Хочу сократить количество кода. У меня идет добавление классов в один List<абстрактный класс>, при добавлении все хорошо, при выводе я делаю проверку соответствие экземпляра класса с его изночальным, типом а не цепочкой классов, если делать проверку через is, то мне отдаст true, если класс можно привести к его родителю, а это не то что мне надо. Я сделал проверку, но я пишу лишний код, и думаю что его можно сократить.
if (figure.Count > 0)
{
foreach (var item in figure)
{
var i = item.GetType();
if (i == typeof(Line))
{
Line line = item as Line;
Console.WriteLine(line.ToString());
}
else if (i == typeof(Ring))
{
Ring ring = item as Ring;
Console.WriteLine(ring.ToString());
}
else if (i == typeof(Square))
{
Square square = item as Square;
Console.WriteLine(square.ToString());
}
else if (i == typeof(Rectangle))
{
Rectangle rectangle = item as Rectangle;
Console.WriteLine(rectangle.ToString());
}
else if (i == typeof(Circle))
{
Circle circle = item as Circle;
Console.WriteLine(circle.ToString());
}
else if (i == typeof(Сircumference))
{
Сircumference circleWithoutArea = item as Сircumference;
Console.WriteLine(circleWithoutArea.ToString());
}
};
}
Ответы (3 шт):
Как вариант
if (figure.Count > 0)
{
var types = new[] { typeof(Line),typeof(Ring),
typeof(Square), typeof(Rectangle), typeof(Circle), typeof(Сircumference) };
foreach (var item in figure)
{
var type = item.GetType();
if (types.Contains(type)) Console.WriteLine(item.ToString());
};
}
В копилочку, как сократить код.
Фрагмент
if (i == typeof(Line))
{
Line line = item as Line;
Console.WriteLine(line.ToString());
}
Можно записать вот так с помощью паттерн-матчинга
if (item is Line line)
{
Console.WriteLine(line);
}
То есть в условии произойдет комбо-действие, одновременно и проверка типа и присваивание значения типизированной переменной. При этом строчка var i = item.GetType() становится лишней.
Не всегда можно завернуть такое в массив (как в ответе @tym32167, но его вариант по-своему хорош и в некоторых случаях именно он и спасает). К тому же вариант с паттерн-матчингом может оказаться быстрее по производительности, чем Contains у массива, но я не проверял.
Проверка if (figure.Count > 0) лишняя, цикл foreach не будет выполняться, если коллекция пуста.
foreach (var item in figure)
{
switch (item)
{
case Line line:
Console.WriteLine(line);
break;
case Ring ring:
Console.WriteLine(ring);
break;
case Square square:
Console.WriteLine(square);
break;
case Rectangle rectangle:
Console.WriteLine(rectangle);
break;
case Circle circle:
Console.WriteLine(circle);
break;
case Сircumference circleWithoutArea:
Console.WriteLine(circleWithoutArea);
break;
}
}
Но это для общего случая, то есть когда в каждом case может быть какой-то свой уникальный код. А для вашего конкретного случая код можно написать так:
foreach (var item in figure)
{
Console.WriteLine(item);
}
Почему я убрал .ToString(), я предположил, что он у вас переопределен, ну с очень большой вероятностью.
public override ToString()
{
// ...
}
А Console.WriteLine() принимает в себя тип object, который неявно наследуют все классы, и для вывода в консоль сам внутри вызыват object.ToString(), который у самого object является virtual. Здесь срабатывает эффект под названием полиморфизм, и отрабатывает именно нужный вам переопределенный метод.
В качестве варианта решения @aepot, если вы работаете на современной версии C# (9+)*, подойдёт такой компактный вариант:
foreach (var item in figure)
if (item is Line or Ring or Square or Rectangle or Circle or Сircumference)
Console.WriteLine(item);
Такой трюк делает виртуальный вызов ToString через ссылку на базовый класс, и будет работать в нормальном случае, если у вас нету перегруженного ToString() (new). С override будет работать в любом случае.
Есть подозрения, что вам в реальности вовсе не нужна проверка типа, и вам достаточно простого кода с неявным виртуальным вызовом ToString:
foreach (var item in figure)
Console.WriteLine(item);
Если же вам реально нужно «достучаться» до перегруженного ToString(), можно использовать Console.WriteLine(((dynamic)item).ToString()). Расходами на dynamic по сравнению со скоростью ввода-вывода вполне можно пренебречь.
Но более хорошей идеей является никогда не перегружать ToString, а только переопределять (то есть, нужно использовать override вместо new).
*Если у вас не .NET 5, то можно просто прописать в .csproj-файле <LangVersion>9.0</LangVersion> в верхней PropertyGroup.
