如何创建和访问在C#中作为参数传递的匿名类的新实例?

前端之家收集整理的这篇文章主要介绍了如何创建和访问在C#中作为参数传递的匿名类的新实例?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我创建了一个函数,它接受一个sql命令并产生输出,然后可以使用它来填充类实例列表.代码工作得很好.我已经包含一个略微简化的版本,无异常处理这里只是为了参考 – 如果你想要正确的问题跳过这个代码.不过,如果你有建议,我都是耳朵.
public List<T> ReturnList<T>() where T : new()
    {
        List<T> fdList = new List<T>();
        myCommand.CommandText = QueryString;
        sqlDataReader nwReader = myCommand.ExecuteReader();
        Type objectType = typeof (T);
        FieldInfo[] typeFields = objectType.GetFields();
        while (nwReader.Read())
        {
            T obj = new T();
            foreach (FieldInfo info in typeFields)
            {
                for (int i = 0; i < nwReader.FieldCount; i++)
                {
                    if (info.Name == nwReader.GetName(i))
                    {
                        info.SetValue(obj,nwReader[i]);
                        break;
                    }
                }
            }
            fdList.Add(obj);
        }
        nwReader.Close();
        return fdList;
    }

正如我所说,这工作很好.但是,由于显而易见的原因,我希望能够使用匿名类调用类似的功能.

问题1:看来我必须在调用我的匿名版本的这个函数时构造一个匿名类实例 – 是吗?示例调用是:

.ReturnList(new { ClientID = 1,FirstName = "",LastName = "",Birthdate = DateTime.Today });

问题#2:我的ReturnList函数的匿名版本在下面.任何人都可以告诉我为什么对info.SetValue的调用什么都不做?它不会返回错误或任何东西,但它也不会更改目标字段的值.

public List<T> ReturnList<T>(T sample) 
    {
        List<T> fdList = new List<T>();
        myCommand.CommandText = QueryString;
        sqlDataReader nwReader = myCommand.ExecuteReader();
        // Cannot use FieldInfo[] on the type - it finds no fields.
        var properties = TypeDescriptor.GetProperties(sample); 
        while (nwReader.Read())
        {
            // No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
            T obj = (T)FormatterServices.GetUninitializedObject(typeof(T)); 
            foreach (PropertyDescriptor info in properties)  
            {
                for (int i = 0; i < nwReader.FieldCount; i++)
                {
                    if (info.Name == nwReader.GetName(i))
                    {
                        // This loop runs fine but there is no change to obj!!
                        info.SetValue(obj,nwReader[i]);
                        break;
                    }
                }
            }
            fdList.Add(obj);
        }
        nwReader.Close();
        return fdList;
    }

有任何想法吗?

注意:当我尝试使用FieldInfo数组,就像上面的函数一样,typeFields数组有零个元素(即使objectType显示字段名称 – 奇怪).因此,我使用TypeDescriptor.GetProperties代替.

关于使用反射或匿名类的任何其他提示和指导在这里是适当的 – 我对C#语言的这个特定的角色来说比较新.

更新:我要感谢Jason解决这个问题的关键.以下是修改后的代码,它将创建匿名类实例的列表,从查询中填充每个实例的字段.

public List<T> ReturnList<T>(T sample)
   {
       List<T> fdList = new List<T>();
       myCommand.CommandText = QueryString;
       sqlDataReader nwReader = myCommand.ExecuteReader();
       var properties = TypeDescriptor.GetProperties(sample);
       while (nwReader.Read())
       {
           int objIdx = 0;
           object[] objArray = new object[properties.Count];
           foreach (PropertyDescriptor info in properties) 
               objArray[objIdx++] = nwReader[info.Name];
           fdList.Add((T)Activator.CreateInstance(sample.GetType(),objArray));
       }
       nwReader.Close();
       return fdList;
   }

请注意,查询已经被构造,并且在以前对该对象的方法调用中初始化了参数.原始代码具有内部/外部循环组合,以便用户可以在其匿名类中具有与字段不匹配的字段.然而,为了简化设计,我决定不允许这样做,而是采用Jason推荐的db字段访问.此外,感谢Dave Markle以及帮助我​​了解更多关于使用Activator.CreateObject()与GenUninitializedObject的权衡.

解决方法

匿名类型封装了一组只读属性.这解释了

>为什么Type.GetFields在匿名类型上调用时返回一个空数组:匿名类型没有公共字段.
>匿名类型的公共属性是只读的,不能通过调用PropertyInfo.SetValue来设置它们的值.如果在匿名类型的属性调用PropertyInfo.GetSetMethod,则将返回null.

其实如果你改变了

var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read()) {
    // No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
    T obj = (T)FormatterServices.GetUninitializedObject(typeof(T)); 
    foreach (PropertyDescriptor info in properties) {
        for (int i = 0; i < nwReader.FieldCount; i++) {
            if (info.Name == nwReader.GetName(i)) {
                // This loop runs fine but there is no change to obj!!
                info.SetValue(obj,nwReader[i]);
                break;
            }
        }
    }
    fdList.Add(obj);
}

PropertyInfo[] properties = sample.GetType().GetProperties();
while (nwReader.Read()) {
    // No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
    T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
    foreach (PropertyInfo info in properties) {
        for (int i = 0; i < nwReader.FieldCount; i++) {
            if (info.Name == nwReader.GetName(i)) {
                // This loop will throw an exception as PropertyInfo.GetSetMethod fails
                info.SetValue(obj,nwReader[i],null);
                break;
            }
        }
    }
    fdList.Add(obj);
}

您将收到一个异常,通知您无法找到属性方法.

现在,为了解决你的问题,你可以做的是使用Activator.CreateInstance.我很抱歉,我太懒了,为你输入代码,但下面将演示如何使用它.

var car = new { Make = "Honda",Model = "Civic",Year = 2008 };
var anothercar = Activator.CreateInstance(car.GetType(),new object[] { "Ford","Focus",2005 });

所以,就像你所做的那样,循环遍历,填满你需要传递给Activator.CreateInstance的对象数组,然后在循环完成后调用Activator.CreateInstance.属性顺序在这里很重要,因为两个匿名类型是相同的,当且仅当它们具有相同数量的具有相同类型和相同名称属性相同的顺序时.

有关更多信息,请参阅MSDN page匿名类型.

最后,这真的是一个旁边,而不是你的问题,但下面的代码

foreach (PropertyDescriptor info in properties) {
    for (int i = 0; i < nwReader.FieldCount; i++) {
        if (info.Name == nwReader.GetName(i)) {
            // This loop runs fine but there is no change to obj!!
            info.SetValue(obj,nwReader[i]);
            break;
        }
    }
}

可以简化

foreach (PropertyDescriptor info in properties) {
            info.SetValue(obj,nwReader[info.Name]);
}
原文链接:https://www.f2er.com/csharp/96289.html

猜你在找的C#相关文章