Làm thế nào để có được loại T từ một thành viên của một lớp hoặc phương thức chung?


674

Giả sử tôi có một thành viên chung trong một lớp hoặc phương thức, vì vậy:

public class Foo<T>
{
    public List<T> Bar { get; set; }

    public void Baz()
    {
        // get type of T
    }   
}

Khi tôi khởi tạo lớp, Ttrở thành MyTypeObject1, vì vậy lớp có thuộc tính danh sách chung : List<MyTypeObject1>. Điều tương tự cũng áp dụng cho một phương thức chung trong một lớp không chung chung:

public class Foo
{
    public void Bar<T>()
    {
        var baz = new List<T>();

        // get type of T
    }
}

Tôi muốn biết, danh sách các lớp của tôi chứa loại đối tượng nào. Vậy thuộc tính danh sách được gọi Barhoặc biến cục bộ baz, chứa loại Tnào?

Tôi không thể làm được Bar[0].GetType(), vì danh sách có thể chứa các phần tử bằng không. Tôi làm nó như thế nào?

Câu trả lời:


693

Nếu tôi hiểu chính xác, danh sách của bạn có cùng tham số loại với chính lớp chứa. Nếu đây là trường hợp, thì:

Type typeParameterType = typeof(T);

Nếu bạn đang ở trong tình huống may mắn có được objectmột tham số loại, hãy xem câu trả lời của Marc .


4
Lol - vâng, rất đúng; Tôi cho rằng OP chỉ có object, IListhoặc tương tự - nhưng đây rất có thể là câu trả lời đúng.
Marc Gravell

32
Tôi thích cách đọc typeofđược. Nếu bạn muốn biết loại T, chỉ cần sử dụng typeof(T):)
demcodemonkey 17/212

2
Tôi thực sự chỉ sử dụng typeof (Type) và nó hoạt động rất tốt.
Anton

1
Tuy nhiên, bạn không thể sử dụng typeof () với một tham số chung.
Reynevan

2
@Reynevan Tất nhiên bạn có thể sử dụng typeof()với một tham số chung. Bạn có ví dụ nào mà nó không hoạt động không? Hoặc bạn đang nhầm lẫn các tham số loại và tài liệu tham khảo?
Luaan

519

(lưu ý: Tôi giả định rằng tất cả các bạn biết là objecthay IListhoặc tương tự, và rằng danh sách này có thể là bất kỳ loại tại thời gian chạy)

Nếu bạn biết đó là một List<T>, thì:

Type type = abc.GetType().GetGenericArguments()[0];

Một lựa chọn khác là nhìn vào bộ chỉ mục:

Type type = abc.GetType().GetProperty("Item").PropertyType;

Sử dụng TypeInfo mới:

using System.Reflection;
// ...
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];

1
Loại type = abc.GetType (). GetGenericArgument () [0]; ==> Chỉ số mảng ngoài giới hạn ...
Patrick Desjardins

28
@Daok: sau đó không phải là Danh sách <T>
Marc Gravell

Cần một cái gì đó cho BindingList hoặc List hoặc bất kỳ đối tượng nào chứa <T>. Những gì tôi đang làm sử dụng BindingListView tùy chỉnh <T>
Patrick Desjardins

1
Hãy dùng thử với BindingList <T>, BindingListView <T> của chúng tôi kế thừa từ BindingList <T> và cả hai tôi đã thử cả hai tùy chọn của bạn và nó không hoạt động. Tôi có thể làm gì đó sai ... nhưng tôi nghĩ giải pháp này hoạt động cho loại Danh sách <T> chứ không phải loại danh sách khác.
Patrick Desjardins

Loại type = abc.GetType (). GetProperty ("Item"). PropertyType; trả về BindingListView <MyObject> thay vì MyObject ...
Patrick Desjardins

49

Với phương pháp mở rộng sau đây, bạn có thể thoát khỏi mà không cần phản ánh:

public static Type GetListType<T>(this List<T> _)
{
    return typeof(T);
}

Hay nói chung hơn:

public static Type GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return typeof(T);
}

Sử dụng:

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

10
Điều này chỉ hữu ích nếu bạn đã biết loại Ttại thời gian biên dịch. Trong trường hợp đó, bạn không thực sự cần bất kỳ mã nào cả.
đệ quy

'trả về mặc định (T);'
tưởng tượng

1
@recursive: Thật hữu ích nếu bạn đang làm việc với một danh sách loại ẩn danh.
JJJ

Tôi đã làm điều tương tự trước khi tôi đọc câu trả lời của bạn nhưng tôi đã gọi cho tôiItemType
chập chững

31

Thử

list.GetType().GetGenericArguments()

7
Danh sách mới <int> (). GetType (). GetGenericArgument () trả về System.Type [1] tại đây với System.Int32 làm mục nhập
Rauhotz

@Rauhotz GetGenericArgumentsphương thức trả về một đối tượng Array Type, trong đó bạn cần phân tích vị trí của Loại chung mà bạn cần. Chẳng hạn như Type<TKey, TValue>: bạn sẽ cần phải GetGenericArguments()[0]có được TKeyloại và GetGenericArguments()[1]để có được TValueloại
GoldBishop

14

Đó là công việc cho tôi. Trong đó myList là một số loại danh sách chưa biết.

IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;

1
Tôi nhận được một lỗi rằng nó yêu cầu một đối số loại (ví dụ <T>)
Joseph Humfrey

Joseph và những người khác, để thoát khỏi lỗi đó là trong System.Collections.
dùng2334883

1
Chỉ cần dòng thứ hai là cần thiết cho tôi. A Listđã là một triển khai IEnumerable, vì vậy các diễn viên dường như không thêm bất cứ điều gì. Nhưng cảm ơn, đó là một giải pháp tốt.
pipedreambomb

10

Nếu bạn không cần toàn bộ biến Loại và chỉ muốn kiểm tra loại bạn có thể dễ dàng tạo biến tạm thời và sử dụng là toán tử.

T checkType = default(T);

if (checkType is MyClass)
{}

9

Bạn có thể sử dụng loại này cho loại trả về của danh sách chung:

public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}

8

Xem xét điều này: Tôi sử dụng nó để xuất danh sách 20 gõ theo cùng một cách:

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);
    }
}

1
Điều này không hoạt động nếu T là một siêu lớp trừu tượng của các đối tượng được thêm thực tế. Chưa kể, chỉ cần new T();làm điều tương tự như (T)Activator.CreateInstance(typeof(T));. Nó yêu cầu bạn thêm where T : new()vào định nghĩa lớp / hàm, nhưng nếu bạn muốn tạo các đối tượng, điều đó vẫn nên được thực hiện.
Nyerguds

Ngoài ra, bạn đang gọi GetTypemột FirstOrDefaultmục nhập dẫn đến một ngoại lệ tham chiếu null tiềm năng. Nếu bạn chắc chắn rằng nó sẽ trả lại ít nhất một mặt hàng, tại sao không sử dụng Firstthay thế?
Mathias Lykkegaard Lorenzen

6

Tôi sử dụng phương pháp mở rộng này để thực hiện một cái gì đó tương tự:

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;
}

Bạn sử dụng nó như thế này:

[TestMethod]
public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes()
{
    typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName()
        .ShouldEqual("Dictionary<String, Dictionary<String, Object>>");
}

Đây không hoàn toàn là những gì bạn đang tìm kiếm, nhưng nó hữu ích trong việc thể hiện các kỹ thuật liên quan.


Xin chào, cảm ơn câu trả lời của bạn, bạn có thể thêm tiện ích mở rộng cho "StripStartingWith" không?
Cedric Arnould

1
@CedricArnould - Đã thêm.
Ken Smith

5

Các GetGenericArgument()phương pháp này phải được thiết lập trên cơ sở Loại sơ thẩm của bạn (có lớp là một lớp generic myClass<T>). Mặt khác, nó trả về một loại [0]

Thí dụ:

Myclass<T> instance = new Myclass<T>();
Type[] listTypes = typeof(instance).BaseType.GetGenericArguments();

5

Bạn có thể lấy loại "T" từ bất kỳ loại bộ sưu tập nào thực hiện IEnumerable <T> với các mục sau:

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;
}

Lưu ý rằng nó không hỗ trợ các loại bộ sưu tập triển khai IEn Countable <T> hai lần, ví dụ:

public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... }

Tôi cho rằng bạn có thể trả về một loạt các loại nếu bạn cần hỗ trợ ...

Ngoài ra, bạn cũng có thể muốn lưu trữ kết quả cho mỗi loại bộ sưu tập nếu bạn thực hiện việc này rất nhiều (ví dụ: trong một vòng lặp).


1
public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}

1
Điều này dường như để giải quyết câu hỏi liệu loại đó có phải là một loại danh sách hay không, nhưng câu hỏi là về cách xác định tham số loại chung nào mà một loại được biết là Danh sách đã được khởi tạo.
Nathan Tuggy

1

Sử dụng giải pháp của 3dGrabber:

public static T GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return default(T);
}

//and now 

var list = new Dictionary<string, int>();
var stronglyTypedVar = list.GetEnumeratedType();

0

Nếu bạn muốn biết loại cơ bản của một tài sản, hãy thử điều này:

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]

0

Đây là cách tôi đã làm nó

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);
}

Sau đó gọi nó như thế này

var item = Activator.CreateInstance(iListType.GetElementType());

HOẶC LÀ

var item = Activator.CreateInstance(Bar.GetType().GetElementType());

-9

Kiểu:

type = list.AsEnumerable().SingleOrDefault().GetType();

1
Điều này sẽ ném NullReferenceException nếu danh sách không có thành phần nào bên trong nó để kiểm tra.
tái lập

1
SingleOrDefault()cũng ném InvalidOperationExceptionkhi có hai hoặc nhiều yếu tố.
devgeezer

Câu trả lời này là sai, như được chỉ ra một cách chính xác bởi \ @rossonomead và \ @devgeezer.
Oliver
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.