如何设置匿名对象的属性值?

这是我的代码示例:

var output = new { NetSessionId = string.Empty }; foreach (var property in output.GetType().GetProperties()) { property.SetValue(output, "Test", null); } 

它发生exception:“找不到属性集方法”。 我想知道如何创建一个具有可设置属性的匿名类型。

谢谢。

匿名类型属性是只读的,无法设置它们。

匿名类型提供了一种将一组只读属性封装到单个对象中的便捷方法,而无需先显式定义类型。 类型名称由编译器生成,在源代码级别不可用。 每个属性的类型由编译器推断。

匿名类型(C#编程指南)

如何设置匿名对象的属性值?

因为我今天被提醒的是,当将reflection与关于如何实现某些事物的知识相结合时,没有什么是真正不可变的(在这种情况下支持匿名类型的只读属性的字段),我认为添加说明如何解释的答案是明智的通过将匿名对象映射到其支持字段,可以更改其属性值。

此方法依赖于编译器用于命名这些后备字段的特定约定: i__Field .NET中的i__Field和Mono上的xxxxx表示属性名称。 如果要更改此约定,则下面的代码将失败(注意:如果您尝试将其提供给非匿名类型,它也将失败)。

 public static class AnonymousObjectMutator { private const BindingFlags FieldFlags = BindingFlags.NonPublic | BindingFlags.Instance; private static readonly string[] BackingFieldFormats = { "<{0}>i__Field", "<{0}>" }; public static T Set( this T instance, Expression> propExpression, TProperty newValue) where T : class { var pi = (propExpression.Body as MemberExpression).Member; var backingFieldNames = BackingFieldFormats.Select(x => string.Format(x, pi.Name)).ToList(); var fi = typeof(T) .GetFields(FieldFlags) .FirstOrDefault(f => backingFieldNames.Contains(f.Name)); if (fi == null) throw new NotSupportedException(string.Format("Cannot find backing field for {0}", pi.Name)); fi.SetValue(instance, newValue); return instance; } } 

样品:

 public static void Main(params string[] args) { var myAnonInstance = new { FirstField = "Hello", AnotherField = 30, }; Console.WriteLine(myAnonInstance); myAnonInstance .Set(x => x.FirstField, "Hello SO") .Set(x => x.AnotherField, 42); Console.WriteLine(myAnonInstance); } 

带输出:

 { FirstField = Hello, AnotherField = 30 } { FirstField = Hello SO, AnotherField = 42 } 

这里可以找到更精细的版本

如果你遇到过需要一个可变类型的情况,而不是乱用Anonymous类型,你可以使用ExpandoObject

示例

 var people = new List { new Person { FirstName = "John", LastName = "Doe" }, new Person { FirstName = "Jane", LastName = "Doe" }, new Person { FirstName = "Bob", LastName = "Saget" }, new Person { FirstName = "William", LastName = "Drag" }, new Person { FirstName = "Richard", LastName = "Johnson" }, new Person { FirstName = "Robert", LastName = "Frost" } }; // Method syntax. var query = people.Select(p => { dynamic exp = new ExpandoObject(); exp.FirstName = p.FirstName; exp.LastName = p.LastName; return exp; }); // or people.Select(p => GetExpandoObject(p)) // Query syntax. var query2 = from p in people select GetExpandoObject(p); foreach (dynamic person in query2) // query2 or query { person.FirstName = "Changed"; Console.WriteLine("{0} {1}", person.FirstName, person.LastName); } // Used with the query syntax in this example, but may also be used // with the method syntax just as easily. private ExpandoObject GetExpandoObject(Person p) { dynamic exp = new ExpandoObject(); exp.FirstName = p.FirstName; exp.LastName = p.LastName; return exp; } 

匿名类型在C#中是不可变的。 我认为你不能改变那里的财产。

我有一个类似的场景,我需要将错误代码和消息分配给所有SHARE特定嵌套属性的众多对象类型,因此我不必复制我的方法以供参考,希望它能帮助其他人:

  public T AssignErrorMessage(T response, string errorDescription, int errorCode) { PropertyInfo ErrorMessagesProperty = response.GetType().GetProperty("ErrorMessage"); if (ErrorMessagesProperty.GetValue(response, null) == null) ErrorMessagesProperty.SetValue(response, new ErrorMessage()); PropertyInfo ErrorCodeProperty = ErrorMessagesProperty.GetType().GetProperty("code"); ErrorCodeProperty.SetValue(response, errorCode); PropertyInfo ErrorMessageDescription = ErrorMessagesProperty.GetType().GetProperty("description"); ErrorMessageDescription.SetValue(response, errorDescription); return response; } public class ErrorMessage { public int code { get; set; } public string description { get; set; } } 

建议:您可以一次设置所有属性。

其他答案正确地表明它们是不可变对象(尽管Alex的答案确实显示了如何获得支持字段,这是一个很好但又很混乱的答案)但它们确实暴露了构造函数,因此您可以创建新实例。

此示例为了简洁而克隆自身,但您可以看到如何使用构造函数从定义的值构建对象。

 var anonymousType = output.GetType(); var properties = anonymousType.GetProperties(); var propertyTypes = properties.Select(p => p.PropertyType).ToArray(); //The constructor has parameters for each property on the type var constructor = anonymousType.GetConstructor(propertyTypes); //clone the existing values to pass to ConstructorInfo var values = properties.Select(p => p.GetValue(output)).ToArray(); var anonymousClone = constructor.Invoke(values); 

一种简单的方法是使用NewtonSoft’JsonConverter( JsonConvert.SerializeObject(anonObject) )序列化Json中的匿名对象。 然后,您可以通过字符串操作更改Json并将其重新序列化为可以分配给旧变量的新匿名对象。

有点curl,但初学者真的很容易理解!