Скажем, у меня есть общий элемент в классе или методе, поэтому:
public class Foo<T>
{
public List<T> Bar { get; set; }
public void Baz()
{
// get type of T
}
}
Когда я создаю экземпляр класса, T
становится MyTypeObject1
, поэтому класс имеет общее свойство списка: List<MyTypeObject1>
. То же самое относится к общему методу в неэквивалентном классе:
public class Foo
{
public void Bar<T>()
{
var baz = new List<T>();
// get type of T
}
}
Я хотел бы знать, какие типы объектов содержится в моем классе. Таким образом, свойство list, называемое Bar
или локальная переменная baz
, содержит тип T
?
Я не могу сделать Bar[0].GetType()
, потому что список может содержать нулевые элементы. Как я могу это сделать?
Если я правильно понимаю, ваш список имеет тот же параметр типа, что и сам контейнерный класс. Если это так, то:
Type typeParameterType = typeof(T);
Если вы находитесь в счастливой ситуации с object
как параметром типа, см. Ответ от Марка.
(примечание: я предполагаю, что все, что вам известно, это object
или IList
или подобное, и что во время выполнения этот список может быть любым типом)
Если вы знаете, что это List<T>
, то:
Type type = abc.GetType().GetGenericArguments()[0];
Другим вариантом является просмотр индексатора:
Type type = abc.GetType().GetProperty("Item").PropertyType;
Использование нового TypeInfo:
using System.Reflection;
// ...
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];
С помощью следующего метода расширения вы можете уйти без отражения:
public static Type GetListType<T>(this List<T> _)
{
return typeof(T);
}
Или более общий:
public static Type GetEnumeratedType<T>(this IEnumerable<T> _)
{
return typeof(T);
}
Использование:
List<string> list = new List<string> { "a", "b", "c" };
IEnumerable<string> strings = list;
IEnumerable<object> objects = list;
Type listType = list.GetListType(); // string
Type stringsType = strings.GetEnumeratedType(); // string
Type objectsType = objects.GetEnumeratedType(); // BEWARE: object
T
во время компиляции. В этом случае вам вообще не нужен код.
Try
list.GetType().GetGenericArguments()
Это работает для меня. Где myList - это какой-то неосознанный вид списка.
IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;
<T>
)
Если вам не нужна полная переменная Type и вы просто хотите проверить тип, вы можете легко создать временную переменную и использовать оператор.
T checkType = default(T);
if (checkType is MyClass)
{}
Рассмотрим это: Я использую его для экспорта 20 типизированных списков таким же образом:
private void Generate<T>()
{
T item = (T)Activator.CreateInstance(typeof(T));
((T)item as DemomigrItemList).Initialize();
Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType();
if (type == null) return;
if (type != typeof(account)) //account is listitem in List<account>
{
((T)item as DemomigrItemList).CreateCSV(type);
}
}
new T();
будет делать то же самое, что и (T)Activator.CreateInstance(typeof(T));
, Это требует, чтобы вы добавили where T : new()
в определение класса / функции, но если вы хотите создавать объекты, это должно быть сделано в любом случае.
GetType
для записи FirstOrDefault
приводит к потенциальному исключению нулевой ссылки. Если вы уверены, что он вернет хотя бы один элемент, почему бы не использовать First
вместо этого?
Вы можете использовать это для возвращаемого типа универсального списка:
public string ListType<T>(T value)
{
var valueType = value.GetType().GenericTypeArguments[0].FullName;
return valueType;
}
Метод GetGenericArgument()
должен быть установлен в базовом типе вашего экземпляра
(класс которого является общим классом myClass<T>
). В противном случае он возвращает тип [0]
Пример:
Myclass<T> instance = new Myclass<T>();
Type[] listTypes = typeof(instance).BaseType.GetGenericArguments();
Я использую этот метод расширения для достижения чего-то похожего:
public static string GetFriendlyTypeName(this Type t)
{
var typeName = t.Name.StripStartingWith("'");
var genericArgs = t.GetGenericArguments();
if (genericArgs.Length > 0)
{
typeName += "<";
foreach (var genericArg in genericArgs)
{
typeName += genericArg.GetFriendlyTypeName() + ", ";
}
typeName = typeName.TrimEnd(',', ' ') + ">";
}
return typeName;
}
public static string StripStartingWith(this string s, string stripAfter)
{
if (s == null)
{
return null;
}
var indexOf = s.IndexOf(stripAfter, StringComparison.Ordinal);
if (indexOf > -1)
{
return s.Substring(0, indexOf);
}
return s;
}
Вы используете это так:
[TestMethod]
public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes()
{
typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName()
.ShouldEqual("Dictionary<String, Dictionary<String, Object>>");
}
Это не совсем то, что вы ищете, но это полезно для демонстрации техник.
Вы можете получить тип "T" из любого типа коллекции, который реализует IEnumerable <T> со следующим:
public static Type GetCollectionItemType(Type collectionType)
{
var types = collectionType.GetInterfaces()
.Where(x => x.IsGenericType
&& x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.ToArray();
// Only support collections that implement IEnumerable<T> once.
return types.Length == 1 ? types[0].GetGenericArguments()[0] : null;
}
Обратите внимание, что он не поддерживает типы коллекций, которые реализуют IEnumerable <T> дважды, например.
public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... }
Я предполагаю, что вы можете вернуть массив типов, если вам нужно поддержать это...
Кроме того, вы можете также кэшировать результат по типу коллекции, если вы делаете это много (например, в цикле).
Использование решения 3dGrabber:
public static T GetEnumeratedType<T>(this IEnumerable<T> _)
{
return default(T);
}
//and now
var list = new Dictionary<string, int>();
var stronglyTypedVar = list.GetEnumeratedType();
Вот как я это сделал
internal static Type GetElementType(this Type type)
{
//use type.GenericTypeArguments if exist
if (type.GenericTypeArguments.Any())
return type.GenericTypeArguments.First();
return type.GetRuntimeProperty("Item").PropertyType);
}
Затем назовите его следующим образом
var item = Activator.CreateInstance(iListType.GetElementType());
ИЛИ
var item = Activator.CreateInstance(Bar.GetType().GetElementType());
Если вы хотите узнать тип базового свойства, попробуйте следующее:
propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]
public bool IsCollection<T>(T value){
var valueType = value.GetType();
return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}
Тип:
type = list.AsEnumerable().SingleOrDefault().GetType();
SingleOrDefault()
также InvalidOperationException
при наличии двух или более элементов.
object
,IList
или подобное, но это вполне может быть правильный ответ.typeof
. Если вы хотите узнать тип T, просто используйтеtypeof(T)
:)