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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17441420/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 09:29:40  来源:igfitidea点击:

How to set value for property of an anonymous object?

c#.netanonymous-types

提问by Leo Vo

this is my code for example:

这是我的代码,例如:

var output = new
{
    NetSessionId = string.Empty
};

foreach (var property in output.GetType().GetProperties())
{
    property.SetValue(output, "Test", null);
}

It occurs an exception: "Property set method not found". I want to know how to create an anonymous type with properties which can be set.

它发生异常:“未找到属性设置方法”。我想知道如何使用可以设置的属性创建匿名类型。

Thanks.

谢谢。

采纳答案by MarcinJuraszek

Anonymous type properties are read only and they cannot be set.

匿名类型属性是只读的,不能设置。

Anonymous types provide a convenient way to encapsulate a set of read-only propertiesinto a single object without having to explicitly define a type first. The type name is generated by the compiler and is not available at the source code level. The type of each property is inferred by the compiler.

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

Anonymous Types (C# Programming Guide)

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

回答by Stay Foolish

Anonymous types are immutable in C#. I don't think you can change the property there.

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

回答by Alex

How to set value for property of an anonymous object?

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

Because I was reminded today that nothing is truly immutable when using reflection in combination with knowledge on how certain things are implemented (backing fields for the read-only properties of anonymous types in this case), I thought it wise to add an answer illustrating how the property values of an anonymous object can be changed, by mapping them to their backing fields.

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

This method relies on a specific convention used by the compiler for naming these backing fields: <xxxxx>i__Fieldin .NET and <xxxxx>on Mono, with the xxxxxrepresenting the property name. If this convention were to change, the code below will fail (note: it will also fail if you try to feed it something that is not an anonymous type).

此方法依赖于编译器用于命名这些支持字段的特定约定:<xxxxx>i__Field在 .NET 和<xxxxx>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<T, TProperty>(
        this T instance,
        Expression<Func<T, TProperty>> 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;
    }
}

Sample:

样本:

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);
}

With output:

有输出:

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

A slightly more elaborate version can be found here

可以在这里找到一个稍微复杂的版本

回答by B.K.

If you ever come across a situation where you need a mutable type, instead of messing around with the Anonymoustype, you can just use the ExpandoObject:

如果您遇到需要可变类型的情况,而不是弄乱Anonymous类型,您可以使用ExpandoObject

Example:

示例

var people = new List<Person>
{
    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;
}

回答by Rakan Murtada

I had a similar scenario where I needed to assign an error code and message to numerous object types that all SHARE specific nested properties so I don't have to duplicate my methods for reference hoping it helps someone else:

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

    public T AssignErrorMessage<T>(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; }
    }

回答by Jono Stewart

A suggestion: you can set all properties at once.

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

The other answers correctly suggest that they are immutable objects (although Alex's answer does show how to get at the backing fields, which is a good, yet messy answer) but they do have constructors exposed, so you can create new instances.

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

Below, the example takes a template instance and creates new instances of it in the CreateFromAnonymousTemplate function. There are downsides, but the biggest benefit (aside from being able to create these objects!) is that you are sticking to the convention that anonymous types should be immutable.

下面的示例采用模板实例并在 CreateFromAnonymousTemplate 函数中创建它的新实例。有缺点,但最大的好处(除了能够创建这些对象!)是你坚持匿名类型应该是不可变的约定。

class Program
{
    static void Main(string[] args)
    {
        // Create a template that defines the anonymous type properties.
        var personTemplate = new { Name = "", Age = 0 };

        var sam = CreateFromAnonymousTemplate(personTemplate, "Sam", 43);
        var sally = CreateFromAnonymousTemplate(personTemplate, "Sally", 24);
    }

    private static Dictionary<Type, ConstructorInfo> _constructors = new Dictionary<Type, ConstructorInfo>();

    // By using T, we get intellisense for our returned objects.
    static T CreateFromAnonymousTemplate<T>(T templateReference, params object[] propertyValues)
    {
        // This is the type of the template. In this case, the anonymous type.
        Type anonymousType = templateReference.GetType();

        ConstructorInfo anonymousTypeConstructor;

        if (_constructors.ContainsKey(anonymousType))
        {
            anonymousTypeConstructor = _constructors[anonymousType];

            if(anonymousTypeConstructor.GetParameters().Length != propertyValues.Length)
                throw new ArgumentException("Invalid initialisation properties. Parameters must match type and order of type constructor.", "propertyValues");
        }
        else
        {
            PropertyInfo[] properties = anonymousType.GetProperties();
            if (properties.Count() != propertyValues.Length)
                throw new ArgumentException("Invalid initialisation properties. Parameters must match type and order of type constructor.", "propertyValues");

            // Retrieve the property types in order to find the correct constructor (which is the one with all properties as parameters).
            Type[] propertyTypes = properties.Select(p => p.PropertyType).ToArray();

            // The constructor has parameters for each property on the type.
            anonymousTypeConstructor = anonymousType.GetConstructor(propertyTypes);

            // We cache the constructor to avoid the overhead of creating it with reflection in the future.
            _constructors.Add(anonymousType, anonymousTypeConstructor);
        }

        return (T)anonymousTypeConstructor.Invoke(propertyValues);
    }
}

回答by aveschini

An easy way could be to serialize the anonymous object in a Json with NewtonSoft'JsonConverter (JsonConvert.SerializeObject(anonObject)). Then you can change the Json via string manipulation and reserialize it into a new anonymous object that you can assign to the old variable.

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

A little convolute but really easy to understand for beginners!

有点复杂,但对于初学者来说真的很容易理解!

回答by Deczaloth

I came here being curious about Anonymous Types, and turned out learning a lot from the answers given.

我来到这里是对Anonymous Types感到好奇,结果从给出的答案中学到了很多东西。

In my answer i will try to synthesize some valuable information i found, and provide information that i did not see in the other answers and that i think will also worth to read for future visitors.

在我的回答中,我将尝试综合我发现的一些有价值的信息,并提供我在其他答案中没有看到的信息,我认为这些信息也值得未来的访问者阅读。



Why is not possible to re-set (out of the box) the values of an anonymously type object?

为什么不能重新设置(开箱即用)匿名类型对象的值?

To better understand the why, it is worth to read what @EricLippert said in a comment to another question about Anonymous Types:

为了更好地理解原因,值得阅读@EricLippert 在对另一个关于匿名类型的问题的评论中所说的内容

Note that anonymous types in VB are allowed to be partially mutated. In VB you get to state which parts of the anonymous type are mutable; the generated code will not use mutable bits as part of a hash code / equality, so you don't get the "lost in the dictionary" problem. We decided to not implement these extensions in C#.

请注意,VB 中的匿名类型允许部分变异。在 VB 中,您可以说明匿名类型的哪些部分是可变的;生成的代码不会使用可变位作为哈希码/等式的一部分,因此您不会遇到“在字典中丢失”的问题。我们决定不在 C# 中实现这些扩展。

Plus the text citedby the Accepted Answerfrom the official C# documentation:

加上文中引用接受的答案来自官方的C#文件

Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first. The type name is generated by the compiler and is not available at the source code level. The type of each property is inferred by the compiler.

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

And just to justify a bit the why it is not possible (out of the box) to set the value of a anonymously typed object, let's see what C# in a Nutshellsays:

为了证明为什么不可能(开箱即用)设置匿名类型对象的值,让我们看看Nutshell 中的 C#怎么说:

[If you define]

[如果你定义]

var dude = new { Name = "Bob", Age = 23 };

The compiler translates this to (approximately) the following:

编译器将其转换为(大约)以下内容:

internal class AnonymousGeneratedTypeName
{

    private string name; // Actual field name is irrelevant
    private int    age;  // Actual field name is irrelevant

    public AnonymousGeneratedTypeName(string name, int age)
    {
        this.name = name; this.age = age;
    }

    public string Name { get { return name; } }

    public int    Age  { get { return age;  } }

    // The Equals and GetHashCode methods are overriden...
    // The ToString method is also overriden.

}
...

var dude = AnonymousGeneratedTypeName ("Bob", 23);


But, is it true that once you set the value of an Anonymous Type it is not possible to modify it?

但是,是不是一旦设置了匿名类型的值就无法修改它?

Well, as we can learn from the answer given by @Alex:

好吧,正如我们可以从@Alex 给出的答案中了解到的:

nothing is truly immutable when using reflection in combination with knowledge on how certain things are implemented (backing fields for the read-only properties of anonymous types in this case).

当将反射与有关如何实现某些事物的知识结合使用时,没有什么是真正不可变的(在这种情况下,匿名类型的只读属性的支持字段)。

If you are curious to see HOW you can modify the value of an anonymously typed object, go and read his answer, it is really worth!

如果您想知道如何修改匿名类型对象的值,请阅读他的答案,这真的很值得!



If at the end, you what to stick with the simplicity in the one liner

如果最后,你要坚持一个班轮的简单性

var dude = new { Name = "Bob", Age = 23 };

and want to be able to modify latter one of dude's properties, you can (in many cases) simply change the var keyword by dynamic. Then you can do

并且希望能够修改 dude 的后一个属性,您可以(在许多情况下)简单地通过动态更改 var 关键字。然后你可以做

dynamic dude = new { Name = "Bob", Age = 23 };

dude.Name = "John"; // Compiles correctly.

But watch out! var and dynamic are not as similar as they seem at a first look. As already noted in a comment to @B.K. by @MikeBeaton

但是要小心!var 和 dynamic 并不像乍一看那么相似。正如@MikeBeaton 在对@BK 的评论中所述

this doesn't work in the case where you need a typed null value? (i.e. an ExpandoObject can't support that, but an anonymous type can)

这在您需要输入空值的情况下不起作用?(即 ExpandoObject 不能支持,但匿名类型可以)

There are some SO posts about dynamic vs var.

有一些关于 dynamic 与 var 的SO帖子