获取泛型类的属性

57

我有一个通用类,以及一个对象值,其中 obj.GetType().GetGenericTypeDefinition() == typeof(Foo<>)

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

我如何从`obj`获取`Items`的值?请记住,`obj`是一个`Object`类型,我无法将`obj`强制转换为`Foo`类型,因为我不知道`T`是什么。
我原本希望使用反射来实现这个目标,但每次执行`GetProperty("Items")`时都会返回null。但是,如果有人知道不使用反射的好方法,请告诉我。
假设我的代码像这样:
//just to demonstrate where this comes from
Foo<int> fooObject = new Foo<int>();
fooObject.Items = someList;
object obj = (object)fooObject;

//now trying to get the Item value back from obj
//assume I have no idea what <T> is
PropertyInfo propInfo = obj.GetType().GetProperty("Items"); //this returns null
object itemValue = propInfo.GetValue(obj, null); //and this breaks because it's null

2
你是在 typeof(Foo<>) 上调用 GetProperty() 吗? - luksan
1
如果您使用的是.NET 4,那么dynamic也可以很好地工作。GetProperty应该没问题。请发布一个简短但完整的程序来演示问题。 - Jon Skeet
@JonSkeet 抱歉,我正在使用3.5版本。上面是示例代码。 - gunr2171
使用您的(几乎)完全一样的[.Net 3.5和MS VC#2k8代码可以正常工作](http://i.stack.imgur.com/ko4wh.png)。 - user7116
@gunr2171:你的例子无法编译,在应用明显的修复后,GetProperty 不会 返回 null。请展示一个简短但完整的程序,确实 演示了这个问题。 - Jon Skeet
如何在不知道封闭泛型类型的情况下访问通用属性 - nawfal
5个回答

99

你应该能够使用:

Type t = obj.GetType();

PropertyInfo prop = t.GetProperty("Items");

object list = prop.GetValue(obj);

当然,你无法直接将其转换为 List<T>,因为你不知道类型 T,但是你仍然可以获取 Items 的值。


编辑:

以下是一个完整的示例,以证明其可行:

// Define other methods and classes here
class Foo<T>
{
    public List<T> Items { get; set; }
}

class Program
{
    void Main()
    {   
        //just to demonstrate where this comes from
        Foo<int> fooObject = new Foo<int>();
        fooObject.Items = new List<int> { 1, 2, 3};
        object obj = (object)fooObject;

        //now trying to get the Item value back from obj
        //assume I have no idea what <T> is
        PropertyInfo propInfo = obj.GetType().GetProperty("Items"); //this returns null
        object itemValue = propInfo.GetValue(obj, null);

        Console.WriteLine(itemValue);
                    // Does not print out NULL - prints out System.Collections.Generic.List`1[System.Int32]


        IList values = (IList)itemValue;
        foreach(var val in values)
            Console.WriteLine(val); // Writes out values appropriately
    }
}

1
你仍然可以将它转换为IList。 - luksan
@luksan 是的,或者是 ICollectionIEnumerable - Reed Copsey
1
根据框架版本,您需要使用GetValue(obj, null) - user7116
我非常满意最终结果(Items的值)是一个对象。此外,这段代码对我来说将返回null。 - gunr2171
1
@gunr2171,只要您的“Items”集合不为空,这应该是有效的。您是否在“obj”实例中验证了“Items”不为空? - Reed Copsey
显示剩余2条评论

18
@ReedCopsey是绝对正确的,但如果你真正想问“如何提取类型的通用细节?”,这里有一些“反射的乐趣”:
public void WhatsaFoo(object obj)
{
    var genericType = obj.GetType().GetGenericTypeDefinition();
    if(genericType == typeof(Foo<>))
    {
        // Figure out what generic args were used to make this thing
        var genArgs = obj.GetType().GetGenericArguments();

        // fetch the actual typed variant of Foo
        var typedVariant = genericType.MakeGenericType(genArgs);

        // alternatively, we can say what the type of T is...
        var typeofT = obj.GetType().GetGenericArguments().First();

        // or fetch the list...
        var itemsOf = typedVariant.GetProperty("Items").GetValue(obj, null);
    }
}

所以我不确定是你的代码还是别的问题,但这是第一个让它工作的代码。(我不确定为什么你在这里使用了-1,但按照我的标准来说没问题) - gunr2171
4
@gunr2171 嗯,可能是因为我并没有真正回答所提出的问题... 耸肩 我不在乎负评,但我确实喜欢谈论有趣的代码。 :) - JerKimball
@sixlettervariables 理解了,虽然可以说我的回答并非完全针对原问题,但确实回答了如何反映类型的通用参数的暗示问题。我“整洁的代码”评论有点轻率,我承认。 - JerKimball
@JerKimball:您的代码比必要的复杂得多,一个简单的 obj.GetType().GetProperty("Items") 就足够了,即使它是一个通用类型。 - user7116
1
@sixlettervariables 再次强调,我并不反对你的观点。但是在原始问题中,“我无法将obj转换为Foo,因为我不知道T是什么。”和“假设我不知道<T>是什么”->这让我觉得他是在问如何确定泛型类的类型变体,如果你有未绑定的泛型版本.....当然,现在我只是在卖弄学问;我完全承认,鉴于各种更新,我在这里的代码是完全没有必要的。 - JerKimball
1
@JerKimball,你的代码帮助我解决了另一个问题,谢谢! - Rajiv

5
像这样的东西应该能解决问题:
var foo = new Foo<int>();
foo.Items = new List<int>(new int[]{1,2,3});

// this check is probably not needed, but safety first :)
if (foo.GetType().GetProperties().Any(p => p.Name == "Items"))
{
    var items = foo.GetType().GetProperty("Items").GetValue(foo, null);
}

2

要成功执行该程序,必须使用System.Reflection命名空间。

此程序为您提供任何泛型类的属性名称和值

您可以在C#在线编译器Rexter工具上检查此代码片段。

using System;
using System.Reflection;

namespace GenericPropertyExample
{
    //Declaring a Sample Class 
    public class class1
    {
        public string prop1 { get; set; }
        public string prop2 { get; set; }

    }
    public class Program
    {
        public static void Main(string[] args)
        {
            //Creating Class Object
            class1 objClass1 = new class1 { prop1 = "value1", prop2 = "value2" };

            //Passing Class Object to GenericPropertyFinder Class
            GenericPropertyFinder<class1> objGenericPropertyFinder = new GenericPropertyFinder<class1>();
            objGenericPropertyFinder.PrintTModelPropertyAndValue(objClass1);
            Console.ReadLine();
        }

        //Declaring a Generic Handler Class which will actually give Property Name,Value for any given class.
        public class GenericPropertyFinder<TModel> where TModel : class
        {
            public void PrintTModelPropertyAndValue(TModel tmodelObj)
            {
                //Getting Type of Generic Class Model
                Type tModelType = tmodelObj.GetType();

                //We will be defining a PropertyInfo Object which contains details about the class property 
                PropertyInfo[] arrayPropertyInfos = tModelType.GetProperties();

                //Now we will loop in all properties one by one to get value
                foreach (PropertyInfo property in arrayPropertyInfos)
                {
                    Console.WriteLine("Name of Property is\t:\t" + property.Name);
                    Console.WriteLine("Value of Property is\t:\t" + property.GetValue(tmodelObj).ToString());
                    Console.WriteLine(Environment.NewLine);
                }
            }
        }
    }
}

-1

嘿,伙计们,我一直在苦恼于通用类型的同一个问题,最终找到了获取值的解决方案 --------做到这一点的方法的小代码片段------------------

    public void printFields()
    {
        // Is the list empty
        if (this.list_.Count == 0)
        {
            //Y => Forced exit no object info
            return;
        }

        try
        {
            // Get first item from list
            T item = this.list_[0];

            // Get the type of object
            //**Type thisType = item.GetType();

            // Get array of all fields
            FieldInfo[] thisFieldInfo = item.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            // Loop through all fields and show its info
            for (int ix = 0; ix < thisFieldInfo.Length; ix++)
            {
                // Get Field value
                String strVal = thisFieldInfo[ix].GetValue(item).ToString();

                // Display item
                Console.WriteLine("'{0}' is a {1} and has value {2}", thisFieldInfo[ix].Name, thisFieldInfo[ix].FieldType, strVal);
            }


        }
        catch (SecurityException e)
        {
            Console.WriteLine("Exception: " + e.Message);
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接