Список нескольких типов данных?
У меня есть два класса:
public class MachineLine
{
public double X1;
public double Y1;
public double X2;
public double Y2;
public double Thickness;
}
public class MachineCircle
{
public double CenterX;
public double CenterY;
public double Radius;
}
Я хочу создать список, который может содержать оба эти файла, но я не хочу, чтобы мой список мог хранить любой другой тип данных. Как это можно сделать?
Ответы
Ответ 1
Самый простой способ сделать это - объявить интерфейс и реализовать оба его типа:
public interface IMachine { … }
public class MachineLine : IMachine
{
public double X1;
public double Y1;
public double X2;
public double Y2;
public double Thickness;
}
public class MachineCircle : IMachine
{
public double CenterX;
public double CenterY;
public double Radius;
}
Затем вы можете сделать это:
List<IMachine> m = new List<IMachine>();
Ответ 2
У вас есть ответы, объясняющие, как это сделать с пустым интерфейсом:
interface IMachineSomething {}
…
var m = new List<IMachineSomething>();
Это прекрасно работает, если вы хотите поместить объекты в этот список. Но что, если вы хотите на самом деле извлечь объекты из списка? Что вы на самом деле могли бы с ними сделать, и какое решение заставит вас сделать это?
Ниже показано, что вы можете сделать еще лучше с непустым интерфейсом.
IMachineSomething
- пустой интерфейс. То есть, если вы действительно хотите что-то сделать с объектами в своем списке, все, что вы увидите, это пустой контракт, определенный этим интерфейсом. Все фактические функциональные возможности соответствуют конкретным типам class
. Поэтому вам сначала нужно проверить их тип, а затем выполнить литье типов для доступа к общедоступным полям:
foreach (IMachineSomething sth in m)
{
if (sth is MachineLine)
{
var machineLine = (MachineLine)sth;
machineLine.DoThis();
}
else if (sth is MachineCircle)
{
var machineCircle = (MachineCircle)sth;
machineCircle.DoThat();
}
else …
}
Поскольку вы работаете с объектно-ориентированным языком, существует гораздо лучшее решение: полиморфизм. То есть, поместите общую функциональность (свойства, а также методы) в интерфейс, так что вам не нужно будет различать типы при просмотре списка:
interface IMachineSomething
{
void DoSomething();
}
class MachineLine : IMachineSomething
{
…
public void DoSomething() { DoThis(); } // example for implicit implementation, or…
}
class MachineCircle : IMachineSomething
{
…
void IMachineSomething.DoSomething() { DoThat(); } // …explicit implementation
}
Это позволяет вам избавиться от проверок типа if (sth is …)
и последующих типов, а также упростить код:
foreach (IMachineSomething sth in m)
{
sth.DoSomething();
}
В цикле больше не нужно заботиться о том, как точно обрабатывается каждый элемент. Все, что нужно знать, должно "что-то делать" с элементом, и сам элемент будет знать, что это значит.
См. также:
Ответ 3
Просто создайте свой собственный интерфейс
public interface IMySillyInterface {}
public class MachineLine : IMySillyInterface
{
public double X1;
public double Y1;
public double X2;
public double Y2;
public double Thickness;
}
public class MachineCircle : IMySillyInterface
{
public double CenterX;
public double CenterY;
public double Radius;
}
List<IMySillyInterface> list = new List<IMySillyInterface>
{
new MachineCircle(),
new MachineLine()
};
Ответ 4
У вас есть 2 способа:
1- Использовать наследование:
public class MachineShape{}
public class MachineLine :MachineShape
{
public double X1;
public double Y1;
public double X2;
public double Y2;
public double Thickness;
}
public class MachineCircle : MachineShape
{
public double CenterX;
public double CenterY;
public double Radius;
}
List<MachineShape> m = new List<MachineShape>();
2- Использовать интерфейс:
public interface IMachineShape{}
public class MachineLine : IMachineShape
{
public double X1;
public double Y1;
public double X2;
public double Y2;
public double Thickness;
}
public class MachineCircle : IMachineShape
{
public double CenterX;
public double CenterY;
public double Radius;
}
List<IMachineShape> m = new List<IMachineShape>();
И я рекомендую наследование в вашем случае...
Ответ 5
Вы можете заставить два класса реализовать один и тот же интерфейс и сделать этот интерфейс содержащим общий тип вашего List
.
Ответ 6
Теперь, с С# 7.0 так же просто, как с помощью Tuple:
public List<(MachineLine line, MachineCircle circle)> machineList { get; set; }
Чтобы добавить данные:
MachineLine theLine = ...;
MachineCircle theCircle = ...;
machineList.Add(theLine, theCircle);
Получить данные:
MachineLine ml = emailSender.ElementAt(3).line;
MachineCircle mc = emailSender.ElementAt(3).circle;
Проще, как один, два, три!