Why can't I set properties or use attributes in C# anonymous types?
Asked Answered
G

1

0

C# anonymous types seem really useful, but I've pretty immediately hit upon an application where I would like to set the property of an anonymous type, and I'd also like to be able to use attributes.

My application is a general purpose stored procedure and SQL executor, which can automatically map C# properties to SQL input and output paramaters with appropriate SqlDbType (e.g. string maps to NVARCHAR(MAX), etc.).

e.g.

    int PersonID = 1234;
    var output = new { GivenName = (string)null, FamilyName = (string)null };
    sqlExecutor.ExecSQL("SELECT @GivenName = GivenName, @FamilyName = FamilyName FROM People WHERE PersonID = @PersonID", new { PersonID }, output);
    string GivenName = output.GivenName;
    string FamilyName = output.FamilyName;

The approach works (also for stored procedures, I just used raw SQL in the above to make it clearer what I am trying to do). But I can only make it work, in exactly the form above, by using the 'bad idea' of setting the backing fields in the anonymous output object (using code in the answer by user Alex to How to set value for property of an anonymous object?).

I can't think of any other way to create such an easy to use, lightweight interface for this kind of problem. It's pretty easy to see that attributes might be useful here too, e.g. to modify the parameter mapping.

So why are anonymous types limited to no setting and no attributes? Those both seem as if they would be useful in reasonable use-cases, and as if they would be easy features to include, given that the basic anonymous type feature is already in the language.

Grube answered 10/6, 2016 at 15:39 Comment(13)
Would dynamic work better here?Portraiture
As with all language features, this probably could be made to work in the compiler. But there's always costs associated too. Then think about what the syntax might look like to do this - by the time you've shoehorned in whatever would be needed to mark your anonymous type in such a fashion, you may as well just create a class anyway.Domingadomingo
In addition to what James said also I think that if you need more then you should go with an explicit named class...I saw too much unreadable code in Java because of (highly abused) anonymous types. Leave them for dirty local cases...Leonteen
Anonymous type properties are read only, they can not be set.Wainwright
The main reason is so that they can be used as key values in groupings, joins, etc. Is there a reason you don't just create an actual type that can have attributes? If you want to propose a syntax for a mutable anonymous type then feel free to post it on connect.microsoft.com.Hyunhz
@James Thorpe I think there would be essentially no costs at all to enabling the setter, since the backing field and the getter are definitely already there? The syntax for attributes I think could also be very straight forward and natural, c.f.: #1217937Grube
Also note from Eric Lippert's answer in the duplicate that it is possible (in fact VB has that feature) but the risks of a mutable type outweigh the benefits in their opinion.Hyunhz
@bmju Yes it's technically possible to have mutable anonymous types, but there are downsides - the biggest of which is the use as a hash key, which was one of the driving forces behind the feature in the first place.Hyunhz
@bmju Read all of the answers - they give the reasons why they chose to make them immutable. I don't think I can give any better reasons other than regurgitating the reasons they give.Hyunhz
To be fair, though, I reopened the question.Hyunhz
Read the last part Anonymous Types (C# Programming Guide)Wainwright
@Nate Barbettini Thank you for the suggestion. I did try a dynamic ExpandoObject, and it associates a typed value with a name, which doesn't work for me (I don't think?) in the case where the value can be null.Grube
@D Stanley This is the post with Eric Lippert's answer: #9044348 Thanks for the heads up about that, and for the other useful info which you've given here.Grube
D
0

Probably you could hack yourself into another solution here, like modifying the generated code of the anonymous class / instance via reflection rewriting or using dynamic stuff like ExpandoObject and/or Dictionary. But here is a cleaner and simpler way - the 'with expression' - which offers 'non-destructive mutation'. It keeps the 'type' and still allows you to mutate the anonymous object (actually it copies everything into a new object (if some of your anonymous type props are reference types, a reference is copied).

The with expression is only supported for - particularly anonymous types - in C# 10, which means .NET 6 framework. So the solution here is only available for the newer solution type. Many of us are stuck with legacy code way back in .NET Framework 4.8 and so on in real world solutions at work.. If you can use C# 10 and .NET 6 at work, consider yourself lucky as you have a mightier toolbelt at hand with C# !

You can from C# 10 use the with expression for anonymous types (and structs and in C# 9 on for records).

Example:

Paste into Linqpad 7: this code in a C# Program and .NET set to Auto

void Main()
{
    var someInstanceOfAnonymousType = new {
     Knight = "Sir Galahad",
     FavoriteColor = "Blue. No RED!",
     Shout = "Aaaargh!"
    }; 
    
    var mutatedInstanceOfAnonymousType = someInstanceOfAnonymousType with
    { Knight = "Sir Lancelot", Shout = "I did not vote for you!" };
    
    mutatedInstanceOfAnonymousType.Dump(); 
}

Note that the with operator will maintain your anonymous type and still let you in an easy way modify any number of backing fields exposed as readonly properties in the anonymous type. Just type on the RHS the instance followed with a 'with' followed with the curly braces and the properties with values you want to set.

Mutating anonymous typed instance with with expression

Disproportionate answered 12/4, 2022 at 21:45 Comment(1)
If you use a tool like IlSpy you can find the 'hidden fields' via reflection and mutate them. This is not recommended at all, just to illustrate it is possible. var knightField = mutatedInstanceOfAnonymousType.GetType().GetField("<Knight>i__Field", BindingFlags.NonPublic | BindingFlags.Instance); knightField.SetValue(mutatedInstanceOfAnonymousType, "Sir Gawain");Disproportionate

© 2022 - 2024 — McMap. All rights reserved.