65

I have the following code snippet:

Expression<Func<TSource, TDest>> expression = model => new TDest{};
// Result: {model => new TestModel(){}}

ReSharper refactors this snippet with RedundantEmptyObjectOrCollectionInitializer setting:

Expression<Func<TSource, TDest>> expression2 = model => new TDest();
// Result: {model => new TestModel()}

After that, my code doesn't work. What influence do the curly braces have on initializing?
I found What is the Difference Between new object() and new {} in C#? on Stack Overflow, but both instances look equal.

expression.GetType().ToString() is equal to expression2.GetType().ToString()

What is the difference between these initializations in expression trees?:

var a = model => new TDest{};
var b = model => new TDest();
cSteusloff
  • 2,487
  • 7
  • 30
  • 51
  • 2
    What do you mean by doesn't work? Can you provide some code to illustrate what happens during the refactor? Think about adding a [mcve]. – TheLethalCoder Apr 09 '18 at 13:37
  • Linqpad generates exactly the same IL, and logically there won't be a difference as they do the same thing (call a constructor with no parameters, and then set 0 properties). Perhaps you've misdiagnosed your issue. As others have said, please provide an MVCE, and a clear explanation of your error. – RB. Apr 09 '18 at 13:38
  • 4
    @RB no, OP is correct - see my answer – Marc Gravell Apr 09 '18 at 13:40
  • 7
    @ngeksyo IMO not a dup in this case - there is an `Expression`-specific nuance at play here – Marc Gravell Apr 09 '18 at 13:40
  • I have always thought that it is 100% identical. It must have something to do with expression. I try to add a MCVE – cSteusloff Apr 09 '18 at 13:40
  • 7
    @cSteusloff they're 100% identical *in regular C# compiled as IL* because there is nothing to do except create the object; however, *semantically* they are different, and the expression-tree compiler is trying to retain the semantic that you expressed, which was: an object initializer with no assignments. – Marc Gravell Apr 09 '18 at 13:50
  • 4
    "*my code doesn't work*" isn't a great expression of what the problem is. Do you get an error message compiling the code? You say "but both instances looks equal." so it seems you are saying the code does work and the results look the same, but then what leads you to believe that your code doesn't work? – Jason Goemaat Apr 09 '18 at 21:12

2 Answers2

104

In regular raw C# the answer would be "nothing". However, when you involve expression trees, there is a difference; as can be seen here

using System;
using System.Linq.Expressions;
public class C<TSource, TDest> where TDest : new() {
    public Expression<Func<TSource, TDest>> Foo() => model => new TDest();
    public Expression<Func<TSource, TDest>> Bar() => model => new TDest {};
}

compiles as:

public Expression<Func<TSource, TDest>> Foo()
{
    ParameterExpression parameterExpression = Expression.Parameter(typeof(TSource), "model");
    Expression arg_2E_0 = Expression.New(typeof(TDest));
    ParameterExpression[] expr_2A = new ParameterExpression[1];
    expr_2A[0] = parameterExpression;
    return Expression.Lambda<Func<TSource, TDest>>(arg_2E_0, expr_2A);
}

public Expression<Func<TSource, TDest>> Bar()
{
    ParameterExpression parameterExpression = Expression.Parameter(typeof(TSource), "model");
    Expression arg_38_0 = Expression.MemberInit(Expression.New(typeof(TDest)), Array.Empty<MemberBinding>());
    ParameterExpression[] expr_34 = new ParameterExpression[1];
    expr_34[0] = parameterExpression;
    return Expression.Lambda<Func<TSource, TDest>>(arg_38_0, expr_34);
}

So one of them involves an Expression.MemberInit (with an empty set of MemberBinding elements) in addition to Expression.New. This can upset any LINQ provider (or any similar expression-tree analysis) that doesn't expect it.

Siyavash Hamdi
  • 2,764
  • 2
  • 21
  • 32
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
5

ReSharper is giving you a better suggestion in how to instantiate the TDest class.

In theory, there isn't any difference as both new TDest () and new TDest {} will provide you with an instance of TDest.

However, when you use the initialization expression, it's because you want to set values of public properties or fields in TDest.

class TDest
{
    public string MyProperty { get; set; }
}

So you could initialize the TDest class setting a value to MyProperty. E.g.:

// Sample 1
new TDest { MyProperty = "This is the value of my property" };

// Sample 2
new TDest() { MyProperty = "This is the value of my property" };

In your case, your class has a parameterless constructor, so both, the punctuators {, } or operators () would be fine.

For simple scenarios where you have a parameterless constructor, you can use only the TDest() short form.

But if you had for example the below class.

class TDest
{
    readonly ISomeInterface _someService;

    public string MyProperty { get; set; }

    public TDest(ISomeInterface someService)
    {
        _someService = someService;
    }
}

And you wanted to initialize MyProperty with something rather than its default initialization value (null, reference types), you could use the full object initialization. E.g.:

// Sample 3
new TDest(new MySomeServiceImplementation()) { MyProperty = "This is the value of my property" };

Hopefully it helps!

To have a better idea, go and have a look at C# Object Initializers.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alexz S.
  • 2,366
  • 4
  • 21
  • 34
  • This explains the useage of in the two expressions in normal cases, but the OP is asking about a specific situation where they _aren't_ equal. – Pyritie Apr 10 '18 at 12:16
  • @Pyritie, I got your point, also the OP's point, however if you look closely the question is "What is the difference between these initializations?: var a = new TDest{}; var b = new TDest();" which I answered above. In the whole picture, adding expression tress to the context, it is as Marc-Gravell mentioned, the compiled code is different semantically. – Alexz S. Apr 10 '18 at 13:15