Roslyn: [Proposal] Object initializers for factory methods

Created on 29 Jan 2015  路  9Comments  路  Source: dotnet/roslyn

Problem

This is another one of the issues when a change to logic significantly changes code structure.
Let's say I had the following:

var x = new X {
    A = a,
    B = b,
    C = { D = { d1, d2 } }
};

Now I decided all X should be created through an XFactory instead.
So I get:

var x = factory.CreateX();
x.A = a;
x.B = b;
x.C.D.Add(d1);
x.C.D.Add(d2);

One small logical change causes a total restructure of not-really-related code.

Potential solution

Provide a way to use object initializer with any existing object.
Example:

var x = factory.CreateX() with {
    A = a,
    B = b,
    C = { D = { d1, d2 } }
};

Keyword with here is provided to remove any syntax ambiguities.

Pros
  1. More consistent syntax everywhere. Shorter syntax for setting a few properties when you need that.

    Cons
  2. New kind of expression-with-side-effects when used on an existing object.

1 - Planning Area-Language Design Feature Request Language-C#

Most helpful comment

It's desirable to allow the same initializers on factories as constructor calls. We need to think this through in conjunction with with expressions, and with object initializers for immutable objects.

All 9 comments

The con could be a big con, but it could be a pro if you expand the scope of the idea. Setting multiple properties on an object after it's been created is not an unheard of thing.

@ashmind,

Can you give an example of what syntax ambiguities does the use of with remove?

Can you elaborate on the cons part?

Are you aware that this:

var x = new X {
    A = a,
    B = b,
    C = { D = { d1, d2 } }
};

is translated into this?

X tmp = new X();
tmp.A = a;
tmp.B = b;
tmp.C.D.Add(d1);
tmp.C.D.Add(d1);
X x = tmp;

The use cases for a block assignment are many. One is the assign properties of return values of methods. But I could also do something like this:

var x = new X { A = 1, B = 2 };
var y = x { A = a, B = b, C = { D = { d1, d2 } } };

I just think you got the motivation all wrong.

Can you give an example of what syntax ambiguities does the use of with remove?

I'm not sure if there are any in the spec at the moment, but allowing x { A = 3 } for arbitrary x seems confusing and may block some other features from being introduced in the future.

Can you elaborate on the cons part?

E.g.

var x = new X();
var y = GetSomething(1, 2, x with { A = 5 }, 4, 5);
// x here has A = 5, which is not immediately obvious

I suppose it can be avoided if initializers are limited to method call results only.

Are you aware that this / is translated into this?

Yes, however I don't want to write it myself for factory methods, given that translator is already in the language.

I just think you got the motivation all wrong.

Can you elaborate? Is your motivation mostly about allowing modification of existing objects?

@ashmind, I still find the whole thing lacks consistency. You talk about side effects when there aren't any, in comparison.

Sure shorthand would be nice. But that's just it. There are no other pros or cons involved.

The proposal also solves the problem of object initializers in using (IDisposable) statement.

Syntax can be similar to the existing (compare x = {...} and C = {...}):

C# using (var x = new X()) { x = { A = 1, B = 2, C = { D = { 3, 4 } }, }; }

IMHO we this problem should be split into two, or at least there are quite a few questions we are discussing:
1) removing "with" keyword should not lead to ambiguity, but only if applied immediately after a method call, it's a visual/readability thing;
2) allow initializer syntax to show up immediately after a variable or field/property doesn't look like a good idea, mostly b/c when we use initializer syntax, we are usually in a situation where the result should be available as soon as possible, like:

cmd.Parameters.Add(cmd.CreateParameter() { ParameterName = "@name", Value = value });

3) but there are cases where applying initializer syntax to a property can be useful, or even a variable, i.e.:

cmd.Parameters {
    cmd.CreateParameter() { ParameterName = "@key", Value = key },
    cmd.CreateParameter() { ParameterName = "@value", Value = value }, 
};

and this lacks some sort of readability;

So far the original idea from @ashmind is the best solution for the questions listed above.

It's desirable to allow the same initializers on factories as constructor calls. We need to think this through in conjunction with with expressions, and with object initializers for immutable objects.

i agree that the 'with' keyword seems unnecessary. i'd like to see this simple extension of 'object initializers' to arbitrary expressions:

  1. new Foo { A = 1 }
  2. new Foo () { A = 1 }
  3. foo { A = 1}
  4. (GetBar().Foos[3]) { A = 1}

Issue moved to dotnet/csharplang #803 via ZenHub

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AdamSpeight2008 picture AdamSpeight2008  路  3Comments

binki picture binki  路  3Comments

nlwolf picture nlwolf  路  3Comments

OndrejPetrzilka picture OndrejPetrzilka  路  3Comments

NikChao picture NikChao  路  3Comments