How to set value for property of an anonymous object?
Asked Answered
N

9

42

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.

Nonparticipating answered 3/7, 2013 at 6:47 Comment(4)
possible duplicate of set value of a property of an anonymous type using reflection / TypeDescriptor is it possible?Avery
I suggest you take a look at the ExpandoObject Class.Avery
As @AlexFilipovici indicated, you can change the property values on an anonymous object by changing the values of their backing fields. This is a fragile method that relies on knowledge of how these backing fields are named to establish the relationship between property and corresponding backing field. I just added an answer to illustrate how this can be done.Remaremain
Alternatively, as a huge hack and at risk of making your code look weird, you can just make the properties of your anonymous object be arrays (of size 1). Then you can just get the array, and set a value to its first position.Nellienellir
E
50

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

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.

Anonymous Types (C# Programming Guide)

Eighteen answered 3/7, 2013 at 6:53 Comment(5)
Please referr to : #2329176Norikonorina
Then how does Json.NET do it? -- It can set property values on anonymous types, so, clearly it can be done.Maines
Short answer: probably reflection. Long answer: I'm not sure the exact way JSON.NET implemented that, but it's possible to set a read-only property after initialization using reflection, expression trees and hand-crafted IL. You have to target the compiler-generated backing field though.Eighteen
See @Alex's answer below!Putative
After C # 10, you can use with keyword for this purpose. https://mcmap.net/q/382407/-how-to-set-value-for-property-of-an-anonymous-objectSupercharge
R
38

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__Field in .NET and <xxxxx> on Mono, with the xxxxx representing 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).

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

Remaremain answered 14/5, 2015 at 16:18 Comment(3)
This is extremely useful, thanks! It's not completely clear to me why anonymous types have to be (in theory) immutable. I was trying to write an automatic stored procedure caller which takes an anonymous input collection and an anonymous output collection (and does simple, automatic C# type to SqlDbType conversion for input and output arguments). This helped me to finish it. (Obviously, there are other ways to do this, but using anonymous types makes the calling syntax extremely simple and friendly to use.)Smolensk
Since I don't know the properties in advance, I've slightly simplified this to take a property name rather than a lambda expression. I guess the advantage of using a lambda expression is automatic existence checking at compile time - but it's also a disadvantage in a context where you don't know what the member properties will be at compile time!Smolensk
Isn't that AnonymousObjectMutator.Set instead of myAnonInstance.Set in your sample?Bacteriology
I
11

If you ever come across a situation where you need a mutable type, instead of messing around with the Anonymous type, you can just use the 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;
}
Inodorous answered 25/5, 2015 at 5:5 Comment(9)
I think 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)Smolensk
@bmju Not sure what you mean. You'd have to give an example.Inodorous
In an anonymous object I can put var MyVar = new { Prop1 = (string) null, Prop2 = (int ?) null }, and Prop1 and Prop2 are then members which have a known type but a null value. (I found this useful for passing a bunch of parameters to a SQL stored procedure caller, you can work out the SqlDbType type from the C# type, and the value can still be null if it needs to be.) In an ExpandoObject a null is a null is a null, it can't have a type.Smolensk
@Smolensk Yeah, but the issue is mutability.Inodorous
Hey! Thanks. I don't think that's the whole story. Technically these things can be mutable. In the underlying framework they are mutable. In VB they are mutable. In C# they chose to make them not mutable, because they had a specific use-case in mind. (At least by the helpful info given by others on this SO question: https://mcmap.net/q/391385/-why-can-39-t-i-set-properties-or-use-attributes-in-c-anonymous-types/#comment63038968_37752175 )Smolensk
(But of course, unless that decision is ever changed, then whilst I love @Alex's answer here as to how to actually do this, of course you can't ever put that into serious production.)Smolensk
@Smolensk Yeah, a lot of things like this are very niche. Most people won't come across situations that require this sort of code and if they do, I'm sure they can refactor it to avoid this.Inodorous
I'm never a huge fan of answers which tell people "you shouldn't want to do this". Let's assume that sometimes people asking questions have good reasons to want to do something, and then tell them whether they can or not, not whether they should! (Yes, 99.9% of people won't ever need this, I agree. But maybe you can use it to do something cool - like say help to write an API which is more useful/neat/easy to code against because it uses this behind the scenes; then I'd say it's a shame if VB, and the underlying runtime, let you do something which is basically intentionally turned off in C#.)Smolensk
Yeah, I'm the same way. I like to explore and come up with code that may seem odd to some, but may be the right solution to an odd scenario. It's fun to explore possibilities.Inodorous
P
4

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

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 be 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:

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#.

Plus the text cited by the Accepted Answer from the official C# documentation:

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 Nutshell says:

[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:

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

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

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)

There are some SO posts about dynamic vs var.

Putative answered 8/11, 2019 at 21:29 Comment(0)
R
2

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.

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.

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);
    }
}
Rhoads answered 8/5, 2017 at 11:28 Comment(2)
Great example of how using var for everything makes your code utterly unreadable.Baer
@Neutrino, fair call, but I don't think it is more readable using full type names. However, I've created a more complete example, which is more clear, and have used type names, in case others agree with you.Rhoads
F
1

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:

    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; }
    }
Foul answered 24/8, 2016 at 18:57 Comment(0)
T
1

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.

A little convolute but really easy to understand for beginners!

Turbosupercharger answered 26/9, 2017 at 11:26 Comment(0)
S
1

After C# 10 (.NET 6.0), you can use with keyword for this purpose.

Anonymous types support non-destructive mutation in the form of with expressions. This enables you to create a new instance of an anonymous type where one or more properties have new values:

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

output = output with
{
    NetSessionId = "Test"
};

For more information, you can visit this link: Anonymous types in C#

Supercharge answered 4/5, 2023 at 7:7 Comment(0)
W
0

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

Woolsey answered 3/7, 2013 at 6:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.