【问题标题】:Dynamically generate object field of generic type动态生成泛型类型的对象字段
【发布时间】:2015-02-26 17:49:36
【问题描述】:

代码:

objectType request = factory.create<objectType>();

public class factory
{
    public static T create<T>() where T : new()
    {
      T obj = new T();
      PropertyInfo propertyInfo = obj.GetType().GetProperty("client_no");
      propertyInfo.SetValue(obj, CLIENT_NUMBER, null);
      return (T)Convert.ChangeType(obj, typeof(T));
     }
}

解释:

我正在创建一个设置 2 个对象属性的通用工厂 fn()。

这些属性在我要初始化的所有对象中都是一致的。

1) 我如何调用我的函数

objectType request = factory.create<objectType>(); // <-- This works

1b) 如果需要,我可以从这里执行以下操作,但这是在我的所有对象中重复的额外代码

request.client_no = CLIENT_NUMBER;

2) 下面是我的工厂 fn()

public static T create<T>() where T : new()
{
  T obj = new T();      

  // Here is where I am having trouble setting client_no I will explain in #3         

  return (T)Convert.ChangeType(obj, typeof(T)); // <-- This works returns generic type
}

3) 我尝试过 PropertyInfo 来设置对象属性如下

PropertyInfo propertyInfo = obj.GetType().GetProperty("client_no");
propertyInfo.SetValue(obj, CLIENT_NUMBER, null);

我也试过this

obj.GetType().GetProperty("client_no").SetValue(obj, CLIENT_NUMBER, null); 

我试过了

T obj = new T();      
var t = typeof(T);
var prop = t.GetProperty("client_no");
prop.SetValue(obj, CLIENT_NUMBER);

4) 这是我收到的错误

对象引用未设置为对象的实例

更多/更新信息:

所以随着进一步的研究。第 3 方对象属性没有 getter 和 setter {get; set;} 这就是 GetType() 和 SetValue() 不起作用的原因。

我的同事指出该物业是一个单一的分配 这就是为什么我们可以这样做

request.client_no = CLIENT_NUMBER;

所以我的问题是如何设置这些属性?

【问题讨论】:

  • 真的不清楚你的updateAccountData 方法的目的是什么。您不能在 C# 中动态创建变量...如果您告诉我们更多关于您实际想要实现的目标,我们可以为您提供更多帮助。
  • 那么你的班级objectName 有你想要填充的属性吗?还是认为这些属性不存在?你最终希望用request做什么?
  • @MattBurland 他们会拥有它,或者我可以在添加项目之前添加一个选项来测试它们是否存在。
  • @Demodave 不容易理解。我们都在猜测你想做什么。
  • @Demodave 我不知道你想在这里实现什么,但如果你认为你需要在对象上动态创建新属性,那么你的角度是错误的。见:XY Problem

标签: c# dynamic generic-programming


【解决方案1】:

使用反射,您可以执行以下操作:

public objectName updateAccountData(int accountId, Dictionary<string,string> accountData)
{
    var request = new objectName();
    var t = typeof(objectName);
    foreach (var k in accountData)
    {
        var prop = t.GetProperty(k.Key);   // you might want to check property exists before trying to set it
        prop.SetValue(request,k.Value);
    }
    return request;
}

这假定您的 objectName 类已经具有您需要填充的所有属性。

请注意,反射比直接访问属性要慢,因此如果此代码位于应用程序的性能关键部分,则可能会出现问题(对其进行分析)。可以很容易地应用一些明显的优化。例如,您可以将从GetProperty 检索到的所有PropertyInfo 对象存储在Dictionary&lt;string,PropertyInfo&gt; 中,以避免必须继续查找它们。更复杂的是,您实际上可以使用表达式树来构建和编译用于属性访问的表达式。

对于一个字段,只需将 t.GetField(k.Key) 替换为 GetProperty

如果您不知道它是字段还是属性(或两者兼有),则可以使用GetMember,然后检查MemberType 来决定。

【讨论】:

  • 这看起来很有潜力。它看起来像是我通过研究类似 PropertyInfo propertyInfo = obj.GetType().GetProperty(key); 解决的另一个问题。 propertyInfo.SetValue(obj, value, null);
  • 不要担心这段代码的性能。您说这是一个 3rd 方 API 对象,通过反射填充对象将比实际调用 3rd 方 API 快几个数量级,因此它永远不会成为您的瓶颈。我知道马特只是为了完整起见才提到它,但请注意他也说过简介它。如果您确实遇到过性能问题,请先进行概要分析,然后再优化真正的问题区域。
  • Reflection 是答案中的代码在运行时用于查找对象属性的内容。分析是针对正在运行的程序运行分析器以找出任何瓶颈所在的行为。如果您有指定的性能目标,您只能知道是否需要进行分析。如果您没有任何性能目标,或者程序是否达到了这些目标,请不要花时间担心性能或分析。
  • @Demodave:在哪里? GetProperty 可能返回 null。正如我的评论所说,您应该检查该属性是否确实存在。这可以像if (prop != null) 一样简单,然后再尝试prop.SetValue。如果prop == null 由您决定,您应该怎么做。
  • @Demodave:那么它不是属性吗?它是一个领域吗?在这种情况下使用GetField 而不是GetProperty。然后你应该可以在GetField返回的FieldInfoSetValue
【解决方案2】:

我看到了接受的答案,但是,这是另一种不涉及直接调用反射而是使用 DLR 的方法。

  public static T create<T>() where T : new()
  {
     T obj = new T();
     ((dynamic)obj).client_no = CLIENT_NUMBER;
     return obj;
  }

MSDN Dynamic Language Runtime Overview

这里最重要的部分是编译器不会自己编写反射调用,而是会处理它。上面的链接讨论了使用它而不是直接进行反射调用的好处。

【讨论】:

  • 虽然这个答案可能是正确且有用的,但最好在其中附上一些解释来解释它如何帮助解决问题。如果有更改(可能不相关)导致它停止工作并且用户需要了解它曾经是如何工作的,这在未来变得特别有用。
  • 我用 MSDN DLR 文档的链接更新了答案。
【解决方案3】:

已解决。方法如下。

我的主要问题是误解,我试图设置的项目不是属性,而是字段。

您可以查看this,更好地了解差异。

这是我修复它的方法:

我设置了泛型的字段类型

public static T create<T>() where T : new()
{
  T obj = new T();

  Type myType = typeof(T);
  FieldInfo myFieldInfo = myType.GetField("client_no");
  myFieldInfo.SetValue(obj, CLIENT_NUMBER);                        

  return obj;
}

特别感谢 @MattBurland 引导我朝着正确的方向前进。

【讨论】:

  • 不确定您认为Convert.ChangeType 正在为您做什么。 obj 已经被声明为 T 类型,所以你应该可以直接返回它。
  • @MattBurland,没错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-19
  • 2018-11-03
  • 2011-02-02
  • 2018-04-07
  • 1970-01-01
相关资源
最近更新 更多