Ogoun
@Ogoun
Programmer

C#. Возможно ли создать список объектов заранее неизвестного типа?

Есть к примеру такой класс:

    public class Value<T>
    {
        public Value()
        {
        }

        public Value(T value)
        {
            this.value = value;
        }

        private T value;
        
    }


Хочу сделать из него коллекцию:

public class Values: List<Value<T>>


, где T заранее неизвестен, но так язык сделать не позволяет. Как реализовать подобное?

В результате хочется чтобы получилось что-то вроде:


Values v=new Values();
v.Add(new Value<int>());
v.Add(new Value<string>());
v.Add(new Value<MyType>());
  • Вопрос задан
  • 16087 просмотров
Пригласить эксперта
Ответы на вопрос 7
@bleykher
Тяжело ответить на вопрос без примера дальнейшего использования списка.
1. Можно сделать просто создать
List<object>
и туда пихать всё, что угодно.
2. Если нужно будет вызывать какой-то метод для объекта в списке, то можно Value унаследовать от класса, содержащего объявление нужного метода (класс BaseValue) и создать список
 List<BaseValue>
.
Ответ написан
Fastto
@Fastto
Хранить объекты разного типа в одном списке имеет смысл, только если они наследуют минимум один общий интерфейс.
Допустим мы решили в списке хранить все формы нашего приложения — у всех форм общий родительский класс, поэтому мы наверняка знаем общие методы. В таком случае с объектами такого списка будет сводится к такому виду:

Copy Source | Copy HTML
  1.  
  2. using System.Reflection;
  3.  
  4. // создаем наши формы
  5. List<object> objects = new List<object>();
  6. objects.Add( new Form1() );
  7. objects.Add( new Form1() );
  8. objects.Add( new Form2() );
  9. objects.Add( new Form3() );
  10.  
  11. //отображаем их
  12. foreach( object obj in objects )
  13. {
  14. Type t= obj .GetType();
  15. Type[] tIncomingParams = {};
  16.  
  17. MethodInfo methodInfo = type.GetMethod( "Show", tIncomingParams );
  18. methodInfo.Invoke( obj, null );
  19. }
  20.  


Если интересно, как проверить наличие конкретного объекта неизвестного типа в таком списке, пишите.
Ответ написан
@Jamon
Java разработчик
а чем не устроил обычный ArrayList?
Ответ написан
Комментировать
@Hanhe
Это возможно, если использовать ковариантность.
Но с ограничениями:
Во-первых, это возможно только в VS2010, компилятор 2008ой этого не поддерживает. Требования к фреймворку уточните на msdn, на сколько я помню 3.5 sp1 поддерживается, хотя я могу и ошибаться, и для ковариантности требуется .net 4.
Во-вторых, невозможно будет параметризовать класс типами-значениями(например int'ом).
Плюс ковариантность налагает некоторые другие ограничения, о которых вы можете прочитать по ссылке на msdn выше.

Пример кода:

Copy Source | Copy HTML
  1. public interface IValue<out T>
  2. {
  3.     T Get();
  4. }
  5.  
  6. public class Value<T>:IValue<T>
  7. {
  8.     public T Get()
  9.     {
  10.         return default(T);
  11.     }
  12. }
  13.  
  14. public class Values: List<IValue<object>>
  15. {
  16.  
  17. }


Пример использования:

Copy Source | Copy HTML
  1. var v = new Values();
  2. v.Add(new Value<string>());
  3. v.Add(new Value<object>());
  4.  
  5. //а вот так нельзя: v.Add(new Value<int>());
  6.  
Ответ написан
burdakovd
@burdakovd
в Java это было бы
List<Value<?>>
Ответ написан
Комментировать
kk86
@kk86
Как вариант, использовать список типа dynamic:

List x = new List();

Но по сути это то же, что и список Object'ов, только ещё медленнее работающий, но не требующий явного приведения типов в коде. Учтите, что использование dynamic может нанести ущерб производительности, т.к. за ним стоит рефлекшн. Пример:

Copy Source | Copy HTML
  1. using System;
  2. using System.Collections.Generic;
  3. namespace Test
  4. {
  5.     class Program
  6.     {
  7.         public static void Main(string[] args)
  8.         {
  9.             List<dynamic> list = new List<dynamic>();
  10.             list.Add(1);
  11.             list.Add("sdfsdf");
  12.  
  13.             foreach (dynamic item in list)
  14.                 Write(item);
  15.  
  16.             Console.ReadKey(true);
  17.         }
  18.  
  19.         public static void Write(Int32 intValue)
  20.         {
  21.             Console.WriteLine("Int: " + intValue);
  22.         }
  23.         public static void Write(String stringValue)
  24.         {
  25.             Console.WriteLine("String: " + stringValue);
  26.         }
  27.     }
  28. }
Ответ написан
Комментировать
Shedal
@Shedal
Если заранее неизвестно, каких типов будут элементы, и при этом их нужно будет экспортировать в какой-то формат, то пишут конвертеры для всех стандартных типов, а для остальных подразумевается, что они должны реализовать какой-то интерфейс.

К примеру, вот так:

protected Dictionary<Type, IExporter> Exporters { get; set; }

public void Export(object value, StreamWriter writer)
{
	IExporter exporter = FindExporter(value.GetType());
	
	if (exporter != null)
	{
		exporter.Export(value, writer);
	}
	else
	{
		writer.WriteString(value.ToString());
	}
}

private IExporter FindExporter(Type type)
{
	IExporter exporter = null;
	
	if(Exporters.ContainsKey(type))
	{
		exporter = Exporters[type];
	}
	else
	{
		if(typeof(IMyFormatExportable).IsAssignableFrom(type))
		{
			exporter = new ExportAwareExporter(type);
		}
	}
	
	return exporter;
}


И для своих классов, которые хотите экспортировать вручную, реализовываете такой интерфейс:

public interface IMyFormatExportable
{
	void Export(StreamWriter writer);
}

public MyClass : IMyFormatExportable
{
	// ...
	
	void Export(StreamWriter writer)
	{
		// writer.Write...(...);
	}
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы