Общий список общих объектов
Скажем, у меня есть объект, представляющий поле данных, этот объект нуждается в следующих свойствах: Name, Type, Value, Length. Вот объект:
class Field<T>
{
public string Name { get; set; }
public Type Type
{
get
{
return typeof(T);
}
}
public int Length { get; set; }
public T Value { get; set; }
}
Я использовал generics, потому что я хочу заставить пользователя кода только иметь возможность назначить значение определенного типа.
Теперь проблема в том, когда я хочу создать список полей.
Если я создам список типа List<Field<object>>
, тогда мы можем назначить любое значение для данного поля в списке, а когда мы запрашиваем Type, мы получим "object".
Дело в том, что в этом списке мне может понадобиться несколько полей, содержащих строки, немногие из которых содержат ints, даты и даже пользовательские объекты, которые, в свою очередь, будут иметь список полей...
Является ли Generics хорошим решением для чего-то подобного? Если да, то как мне его реализовать? Если нет, то что лучше?
--- EDIT ---
Просто добавьте еще немного фона:
1. Мне может понадобиться список полей, и каждое поле будет содержать разные типы данных, например:
List<Field<object>> lst = new List<Field<object>>();
lst.Add(new Field<string>());
lst.Add(new Field<int>());
lst.Add(new Field<SomeObjectFromMyApp>());
2. Позже мне придется запросить эти объекты и их атрибуты автоматически в цикле, что-то вроде этого:
foreach(Field<object> fld in lst)
{
Type t = fld.Type;
//do some other stuff
}
Ответы
Ответ 1
Да, дженерики - хороший выбор. Ключом к достижению безопасности типа (и идентификация типа с атрибутом Type
является добавление абстракции между списком и классом Field<T>
.
Имейте Field<T>
реализовать интерфейс IField
. Этот интерфейс не нуждается ни в каких членах.
Затем объявите свой список как List<IField>
.
Таким образом вы ограничиваете список только полями, но каждое поле может быть другого типа.
Чтобы затем прочитать значения позже, просто сделайте
foreach(var field in list)
{
var type = field.Type;
....
}
Ответ 2
Я предлагаю вам определить интерфейс, и поле реализует этот интерфейс
public interface IField
{
}
public class Field<T> : IField
{
public string Name { get; set; }
public Type Type
{
get
{
return typeof(T);
}
}
public int Length { get; set; }
public T Value { get; set; }
}
чтобы вы могли написать этот код:
var list = new List<IField>();
теперь этот список может содержать любой объект типа Field<T>
Ответ 3
Как уже упоминалось несколько комментаторов, вы не можете получить доступ к свойству Type, если создаете пустой интерфейс, поэтому я бы предпочел сделать:
public interface IField
{
Type Type { get; }
string Name { get; set; }
int Length { get; set; }
}
public class Field<T> : IField
{
public string Name { get; set; }
Type IField.Type => typeof(T);
public int Length { get; set; }
public T Value { get; set; }
public override string ToString()
{
return Value.ToString();
}
}
Затем вы можете проверить, какое значение имеет свойство value, и привести объект к нужному типу:
class Program
{
static void Main(string[] args)
{
var fieldList = new List<IField>()
{
new Field<string>()
{
Value = "Hello World!",
Length = 12,
Name = "A string"
},
new Field<int>()
{
Value = 4711,
Length = sizeof(int),
Name = "An integer value"
},
new Field<double>()
{
Value = 2.4,
Length = sizeof(double),
Name = "A double value"
},
};
foreach (var field in fieldList)
{
if (field.Type == typeof(string))
{
PrintField(field, "String value:");
}
else if (field.Type == typeof(int))
{
PrintField(field, "Integer value:");
}
else if (field.Type == typeof(double))
{
PrintField(field, "Double value:");
}
}
}
static void PrintField(IField field, string info)
{
Debug.WriteLine(info);
Debug.WriteLine($"\tName: {field.Name}, Length: {field.Length}, Value: {field}");
}
Код производит следующий вывод:
// String value:
// Name: A string, Length: 12, Value: Hello World!
// Integer value:
// Name: An integer value, Length: 4, Value: 4711
// Double value:
// Name: A double value, Length: 8, Value: 2,4