Non-read only alternative to anonymous types
Asked Answered
T

8

48

In C#, an anonymous type can be as follows:

method doStuff(){
     var myVar = new {
         a = false, 
         b = true
     }

     if (myVar.a) 
     {
         // Do stuff             
     }
}

However, the following will not compile:

method doStuff(){
     var myVar = new {
         a = false, 
         b = true
     }

     if (myVar.a) 
     {
         myVar.b = true;
     }
}

This is because myVar's fields are read-only and cannot be assigned to. It seems wanting to do something like the latter is fairly common; perhaps the best solution I've seen is to just define a struct outside the method.

However, is there really no other way to make the above block work? The reason it bothers me is, myVar is a local variable of this field, so it seems like it should only be referred to inside the method that uses it. Besides, needing to place the struct outside of the method can make the declaration of an object quite far from its use, especially in a long method.

Put in another way, is there an alternative to anonymous types which will allow me to define a "struct" like this (I realize struct exists in C# and must be defined outside of a method) without making it read-only? If no, is there something fundamentally wrong with wanting to do this, and should I be using a different approach?

Tremor answered 28/1, 2012 at 8:7 Comment(1)
Anonymous types are designed, for once only, quick use. If your use is more complicated than this, there are all readsons to create one named type.Apennines
V
40

No, you'll have to create your own class or struct to do this (preferrably a class if you want it to be mutable - mutable structs are horrible).

If you don't care about Equals/ToString/GetHashCode implementations, that's pretty easy:

public class MyClass {
    public bool Foo { get; set; }
    public bool Bar { get; set; }
}

(I'd still use properties rather than fields, for various reasons.)

Personally I usually find myself wanting an immutable type which I can pass between methods etc - I want a named version of the existing anonymous type feature...

Viradis answered 28/1, 2012 at 8:14 Comment(9)
What I find myself wanting is a quick shorthand for declaring a struct with particular fields, and an auto-generated constructor which takes such fields as parameters. Note, btw, that if a struct doesn't have any member functions that modify this or properties that access outside entitites, an "immutable" struct and a "mutable" one will be indistinguishable to any code which doesn't directly attempt to modify its fields. I can see arguments against trying to modify struct fields if one doesn't know what one is doing, but that's not an argument for having structs forbid such modification.Earthwork
@supercat: I've seen enough problems caused by mutable structs to avoid them in all but the most specialized of situations. Such situations definitely do exist, but I find them rare. I suspect we'll have to agree to disagree on whether mutable structs are a good idea, given your answer.Viradis
How many of those problems did not involve a struct method which wrote this? Can you point me to any description of any (non-this-writing) problems you've seen which are not a clear results of someone who didn't understand how structs work (e.g. grabbing something from a collection, modifying that thing, and blithely expecting it to update the collection--something which may or may not work with classes, and certainly won't work with structs)?Earthwork
@Earthwork - while you may know all possible pitfalls of mutable structs, can you guarantee that every developer who maintains or (if public) consumes your code will be aware of these? While I'm not a believer in coding to the lowest common denominator, mutable structs are the opposite of my preferred "pit of success."Meganmeganthropus
@supercat: Just keep an eye out for them on Stack Overflow. It's not just when things compile and then don't work as expected - it's when people expect to be able to modify things in situations where it really wouldn't help them...Viradis
@JonSkeet: No competent programmer should expect that thing=Collection[index]; thing.someProperty=whatever; is going to safely modify Collection[index].someProperty; unless he has examined enough other code to ensure that's the case. Why should one expect that a programmer who don't understand structs would be competent to recognize whether or not the above code would be a safe way to update a property of a class-type instance? As for people having "mutable struct trouble" on stack overflow, they all seem to fall into three categories: this mutations that don't work, ...Earthwork
...code which attempts to mutate struct fields but won't compile, or code which uses behavior that is ineffective with structs and dangerous with classes. I would suggest that the first problem would be solved by recommending "Don't modify this without a good reason."; the second problem isn't made any worse with mutable structs than immutable ones; the third problem is far less bad with mutable structs than with mutable classes.Earthwork
@supercat: As I said, I don't expect we'll end up agreeing. I'll add one more category though: structs which are mutable and implement a mutating interface, leading to different behaviour when they're boxed from when they're not boxed...Viradis
@JonSkeet: Those fall into the category of structs with methods that modify this; we're in agreement that those are problematic. Our one point of disagreement is that I think that structs should in many cases expose public fields, since one is allowed to write public fields only in those cases where one would be allowed to say structVar = structVar.withField(whatever);, and with essentially the same semantics, except that the field-write will be faster, different fields can be written in different threads without interference, and same-field writes can be resolved via Interlocked.Earthwork
R
27

Is there an alternative to anonymous types which will allow me to concisely define a simple "record" type like this without making it read-only?

No. You'll have to make a nominal type.

If no, is there something fundamentally wrong with wanting to do this?

No, it's a reasonable feature that we have considered before.

I note that in Visual Basic, anonymous types are mutable if you want them to be.

The only thing that is really "fundamentally wrong" about a mutable anonymous type is that it would be dangerous to use one as a hash key. We designed anonymous types with the assumptions that (1) you're going to use them as the keys in equijoins in LINQ query comprehensions, and (2) in LINQ-to-Objects and other implementations, joins will be implemented using hash tables. Therefore anonymous types should be useful as hash keys, and mutable hash keys are dangerous.

In Visual Basic, the GetHashCode implementation does not consume any information from mutable fields of anonymous types. Though that is a reasonable compromise, we simply decided that in C# the extra complexity wasn't worth the effort.

Roid answered 30/1, 2012 at 14:47 Comment(6)
I hadn't realized vb.net implemented anonymous types using classes with mutable properties. That seems like the worst possible choice. It loses all the performance advantages one would get from an exposed field (readonly or not), all of the semantic advantages one would get from a mutable struct (protected against mutation by any code which wouldn't have sufficient access to replace the instance with a new one) or an immutable type.Earthwork
@supercat: they are optionally mutable in VB. If you don't like the feature, you don't have to use it.Roid
Ah, I see. Properties declared as "key" aren't mutable--only those that aren't. I'm still not sure what was gained by using mutable properties instead of fields for the rest. A member needs to be a property instead of a field if a class or (possibly-future) derivative might want non-default behavior, but since anonymous classes' mutable members will always use the default behaviors I'm not clear what's gained by making them properties.Earthwork
@supercat: One reasonable reason for making them properties is only properties can be bound in WPF. Thus using properties makes anonymous classes useful as a data source for WPF. While that might not be the reason the language designers/impelenters made the decision, it does support said decision.Francoise
@KevinCathcart: I've not used WPF; is the idea that it uses Reflection to try to fetch properties from an attached class instance? What expectations does WPF have with regard to mutability? Anonymous types certainly don't have the infrastructure necessary to support safe mutation in the context of asynchronous access.Earthwork
It does use reflection. With regard to muatability, that depends on the binding mode. "OneWay" requires only that the property can be read, "TwoWay" requires reading and writing, and "OneWayToSource" requires only a writable property. The anonymous classes don't inplement IPropertyChangeNotifier, so WPF is not automatically notified on change, but depending on use that may not be a problem, and WPF can always be manually be instructed to rebind. In many applications once a datasource is bound all further access occurs on the UI thread thus avoiding concurrent access concerns.Francoise
S
16

In C# 7 we can leverage named tuples to do the trick:

(bool a, bool b) myVar = (false, true);

if (myVar.a)
{
    myVar.b = true;
}
Strikebreaker answered 12/11, 2017 at 9:36 Comment(2)
:( I was hoping I could use this feature in C# 6, I guess I'm out of luck for my current project... (Unless I can convince some people to upgrade)Ecuador
This is a great idea and works just fine for what I needed, but be aware of two minor details when using Visual Studio. 1) ToString() just shows the values (false, true) although you can expand to see the members. 2) You can't rename the members and have it ripple through.Improper
E
3

For the above types of operation, you should define your own mutable STRUCT. Mutable structs may pose a headache for compiler writers like Eric Lippert, and there are some unfortunate limitations in how .net handles them, but nonetheless the semantics of mutable "Plain Old Data" structs (structs in which all fields are public, and the only public functions which write this are constructors, or are called exclusively from constructors) offer far clearer semantics than can be achieved via classes.

For example, consider the following:

struct Foo {
  public int bar; 
  ...other stuff;
}
int test(Action<Foo[]> proc1, Action<Foo> proc2)
{
  foo myFoos[] = new Foo[100];
  proc1(myFoos);
  myFoos[4].bar = 9;
  proc2(myFoos[4]); // Pass-by-value
  return myFoos[4].bar;
}

Assuming there's no unsafe code and that the passed-in delegates can be called and will return in finite time, what will test() return? The fact that Foo is a struct with a public field bar is sufficient to answer the question: it will return 9, regardless of what else appears in the declaration of Foo, and regardless of what functions are passed in proc1 and proc2. If Foo were a class, one would have to examine every single Action<Foo[]> and Action<Foo> that exists, or will ever exist, to know what test() would return. Determining that Foo is a struct with public field bar seems much easier than examining all past and future functions that might get passed in.

Struct methods which modify this are handled particularly poorly in .net, so if one needs to use a method to modify a struct, it's almost certainly better to use one of these patterns:

  myStruct = myStruct.ModifiedInSomeFashion(...);  // Approach #1
  myStructType.ModifyInSomeFashion(ref myStruct, ...);  // Approach #2

than the pattern:

  myStruct.ModifyInSomeFashion(...);

Provided one uses the above approach to struct-modifying patterns, however, mutable structs have the advantage of allowing code which is both more efficient and easier to read than immutable structs or immutable classes, and is much less trouble-prone than mutable classes. For things which represent an aggregation of values, with no identity outside the values they contain, mutable class types are often the worst possible representation.

Earthwork answered 29/1, 2012 at 20:0 Comment(2)
"Clearer semantics" is obviously in the eye of the beholder, but I disagree the structs with all public members will meet the expectation of many beginning programmers. The semantics of a mutable struct passed by value has many subtle gotchas, as indicated by the obtuse modify pattern recommended in this response. Public fields also breaks the struct's encapsulation, which is one of the major benefits of OOP. Go with Eric and the other language designers on this one -- just don't do it.Gavriella
@JohnMelville: A struct with public fields behaves essentially like a bunch of storage locations stuck together with duct tape. In cases where one wants to stick a bunch of storage locations together with duct tape, code using which actually uses BoSLSTwDT (structs) will be clearer than code which tries to use some other type to simulate them. The primary thing that makes structs confusing is the effort by the language to pretend that they are a form of Object.Earthwork
B
3

You won't be able to get the nice initialization syntax but the ExpandoObject class introduced in .NET 4 would serve as a viable solution.

dynamic eo = new ExpandoObject();

eo.SomeIntValue = 5;
eo.SomeIntValue = 10; // works fine
Brewage answered 7/6, 2016 at 20:59 Comment(1)
I don't think ExpandoObject supports a named, typed, null-valued property (since it's just a dictionary, and all null values will be untyped), whereas an anonymous type does.Maxie
W
1

I didn't see it in any of the answers, but C#'s with-expression, introduced with records, can also help you on an anonymous type, in this case like

var myVar = new {
  a = false, 
  b = true
}

if (myVar.a) 
{
  return myVar with { b = true };
}

return myVar;

Mind you, this will not mutate myVar, but you'll get a new instance with the old values with the modified ones overriding any previous value of the property.

Wavemeter answered 13/6, 2024 at 13:49 Comment(0)
I
0

I know it is really old question but how about replacing whole anonymous object: `

if (myVar.a)
{
    myVar = new
    { a = false, b = true };
}

`

Inglorious answered 9/3, 2022 at 22:55 Comment(0)
P
-1

I find it really annoying that you can't set anonymous properties as read/write as you can in VB - often I want to return data from a database using EF/LINQ projection, and then do some massaging of the data in c# that can't be done at the database for whatever reason. The easiest way to do this is to iterate over existing anonymous instances and update properties as you go. NOTE this is not so bad now in EF.Core, as you can mix db functions and .net functions in a single query finally.

My go-to workaround is to use reflection and will be frowned upon and down-voted but works; buyer beware if the underlying implementation changes and all your code breaks.

public static class AnonClassHelper {

    public static void SetField<T>(object anonClass, string fieldName, T value) {
        var field = anonClass.GetType().GetField($"<{fieldName}>i__Field", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

        field.SetValue(anonClass, value);
    }

}
// usage
AnonClassHelper.SetField(inst, nameof(inst.SomeField), newVal);

An alternative I have used when dealing with strings is to make properties of type StringBuilder, then these individual properties will be settable via the StringBuilder methods after you have an instance of your anonymous type.

Pascual answered 24/2, 2020 at 20:33 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.