Kiểm tra nếu đối tượng thuộc loại chung trong C #


134

Tôi muốn thực hiện một thử nghiệm nếu một đối tượng thuộc loại chung chung. Tôi đã thử những cách sau mà không thành công:

public bool Test()
{
    List<int> list = new List<int>();
    return list.GetType() == typeof(List<>);
}

Tôi đang làm gì sai và làm thế nào để tôi thực hiện bài kiểm tra này?

Câu trả lời:


201

Nếu bạn muốn kiểm tra xem đó có phải là một thể loại chung không:

return list.GetType().IsGenericType;

Nếu bạn muốn kiểm tra nếu đó là chung chung List<T>:

return list.GetType().GetGenericTypeDefinition() == typeof(List<>);

Như Jon chỉ ra, điều này kiểm tra sự tương đương loại chính xác. Trả về falsekhông nhất thiết có nghĩa là list is List<T>trả về false(tức là đối tượng không thể được gán cho một List<T>biến).


9
Điều đó sẽ không phát hiện ra các kiểu con mặc dù. Xem câu trả lời của tôi. Giao diện cũng khó hơn nhiều :(
Jon Skeet

1
Cuộc gọi đến GetGenericTypeDefDef sẽ thực hiện nếu đó không phải là một loại chung. Hãy chắc chắn rằng bạn kiểm tra đầu tiên.
Kilhoffer

85

Tôi giả sử rằng bạn không chỉ muốn biết loại đó là chung chung, nhưng nếu một đối tượng là một thể hiện của một loại chung cụ thể, mà không biết các đối số loại.

Thật không đơn giản, thật không may. Nó không quá tệ nếu loại chung là một lớp (như trong trường hợp này) nhưng khó hơn cho các giao diện. Đây là mã cho một lớp:

using System;
using System.Collections.Generic;
using System.Reflection;

class Test
{
    static bool IsInstanceOfGenericType(Type genericType, object instance)
    {
        Type type = instance.GetType();
        while (type != null)
        {
            if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == genericType)
            {
                return true;
            }
            type = type.BaseType;
        }
        return false;
    }

    static void Main(string[] args)
    {
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new List<string>()));
        // False
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new string[0]));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList()));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList<int>()));
    }

    class SubList : List<string>
    {
    }

    class SubList<T> : List<T>
    {
    }
}

EDIT: Như đã lưu ý trong các bình luận, điều này có thể hoạt động cho các giao diện:

foreach (var i in type.GetInterfaces())
{
    if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
}

Tôi có một nghi ngờ lén lút có thể có một số trường hợp khó xử xung quanh vấn đề này, nhưng tôi không thể tìm thấy nó thất bại ngay bây giờ.


2
Chỉ cần phát hiện ra một vấn đề với điều này. Nó chỉ đi xuống một dòng thừa kế. Nếu, trên đường đi, bạn có một cơ sở với cả lớp cơ sở giao diện mà bạn đang tìm kiếm, điều này chỉ đi xuống đường dẫn lớp.
Groxx

1
@Groxx: Đúng. Tôi vừa phát hiện ra rằng tôi đã đề cập đến điều đó trong câu trả lời: "Không tệ lắm nếu loại chung là một lớp (như trong trường hợp này) nhưng khó hơn cho các giao diện. Đây là mã cho một lớp"
Jon Xiên

1
Nếu bạn không có cách nào để biết <T> thì sao? Giống như, nó có thể là int hoặc chuỗi, nhưng bạn không biết điều đó. Điều này tạo ra, có vẻ như, phủ định sai ... vì vậy bạn không có T để sử dụng, bạn chỉ xem qua các thuộc tính của một số đối tượng và một là một danh sách. Làm thế nào để bạn biết nó là một danh sách để bạn có thể hủy bỏ nó? Ý tôi là, bạn không có T ở bất cứ đâu cũng không phải là loại để sử dụng. Bạn có thể đoán mọi loại (có phải là Danh sách <int>? Có phải là Danh sách <chuỗi> không?) Nhưng điều bạn muốn biết là DANH SÁCH NÀY LÀ NÀY? Câu hỏi đó có vẻ khó trả lời.

@RiverC: Vâng, bạn nói đúng - nó khá khó để trả lời, vì nhiều lý do. Nếu bạn chỉ nói về một lớp học, nó không quá tệ ... bạn có thể tiếp tục đi lên cây thừa kế và xem liệu bạn đánh List<T>ở dạng này hay dạng khác. Nếu bạn bao gồm các giao diện, nó thực sự khó khăn.
Jon Skeet

3
bạn không thể thay thế vòng lặp IsInstanceOfGenericTypebằng một cuộc gọi đến IsAssignableFromthay cho toán tử đẳng thức ( ==)?
slawekwin

7

Bạn có thể sử dụng mã ngắn hơn bằng cách sử dụng althougth, điều này có thể chậm hơn so với phản xạ thuần túy:

public static class Extension
{
    public static bool IsGenericList(this object o)
    {
       return IsGeneric((dynamic)o);
    }

    public static bool IsGeneric<T>(List<T> o)
    {
       return true;
    }

    public static bool IsGeneric( object o)
    {
        return false;
    }
}



var l = new List<int>();
l.IsGenericList().Should().BeTrue();

var o = new object();
o.IsGenericList().Should().BeFalse();

7

Đây là hai phương thức mở rộng yêu thích của tôi bao gồm hầu hết các trường hợp kiểm tra loại chung:

Hoạt động với:

  • Nhiều giao diện (chung)
  • Nhiều lớp cơ sở (chung)
  • Có một tình trạng quá tải sẽ loại ra loại chung chung cụ thể nếu nó trả về đúng (xem thử nghiệm đơn vị cho các mẫu):

    public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
    {
        Type concreteType;
        return typeToCheck.IsOfGenericType(genericType, out concreteType); 
    }
    
    public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType)
    {
        while (true)
        {
            concreteGenericType = null;
    
            if (genericType == null)
                throw new ArgumentNullException(nameof(genericType));
    
            if (!genericType.IsGenericTypeDefinition)
                throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
    
            if (typeToCheck == null || typeToCheck == typeof(object))
                return false;
    
            if (typeToCheck == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if (genericType.IsInterface)
                foreach (var i in typeToCheck.GetInterfaces())
                    if (i.IsOfGenericType(genericType, out concreteGenericType))
                        return true;
    
            typeToCheck = typeToCheck.BaseType;
        }
    }

Đây là một thử nghiệm để chứng minh chức năng (cơ bản):

 [Test]
    public void SimpleGenericInterfaces()
    {
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>)));
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>)));

        Type concreteType;
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType));
        Assert.AreEqual(typeof(IEnumerable<string>), concreteType);

        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType));
        Assert.AreEqual(typeof(IQueryable<string>), concreteType);


    }

0
return list.GetType().IsGenericType;

3
Nó đúng cho một câu hỏi khác. Đối với câu hỏi này, điều đó không chính xác, vì nó chỉ giải quyết (ít hơn đáng kể) một nửa vấn đề.
Groxx

1
Câu trả lời Stan R của nào trong thực tế câu trả lời những câu hỏi như đặt ra, nhưng những gì OP thực sự có nghĩa là "Thử nghiệm nếu đối tượng là một đặc biệt kiểu chung chung trong C #", mà câu trả lời này thực sự là không đầy đủ.
yoyo

mọi người đang bỏ phiếu cho tôi vì tôi đã trả lời câu hỏi trong ngữ cảnh "là" loại chung chung chứ không phải "thuộc loại" chung chung. Tiếng Anh là ngôn ngữ thứ 2 của tôi và các sắc thái ngôn ngữ như vậy thường xuyên vượt qua tôi, để bảo vệ, OP không yêu cầu kiểm tra cụ thể đối với một loại cụ thể và trong tiêu đề yêu cầu "là" loại chung chung ... không chắc tại sao tôi xứng đáng bị hạ bệ một câu hỏi mơ hồ.
Stan R.

2
Bây giờ bạn biết điều đó và bạn có thể cải thiện câu trả lời của bạn để cụ thể và chính xác hơn.
Peter Ivan
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.