Diamond Syntax in C#
Asked Answered
P

4

20

Java 7 now has this "diamond syntax" where I can do things like ArrayList<int> = new ArrayList<>();

I'm wondering if C# has a similar syntax that I can take advantage of.
For example, I have this part of a class:

class MyClass
{
    public List<double[][]> Prototypes; // each prototype is a array of array of doubles

    public MyClass()
    {
        Prototypes = new List<double[][]>; // I'd rather do List<>, in case I change the representation of a prototype later
    }
}

Does anyone know if this is possible, and if so, how I might go about using it?

Petcock answered 18/7, 2013 at 18:39 Comment(1)
By the way, don't use arrays of arrays. Create a proper data model instead.Lira
S
14

No, there's nothing quite like the diamond syntax in C#. The closest you could come would be to have something like this:

public static class Lists
{
    public static List<T> NewList<T>(List<T> ignored)
    {
        return new List<T>();
    }
}

Then:

public MyClass()
{
    ProtoTypes = Lists.NewList(ProtoTypes);
}

That just uses normal generic type inference for methods to get T. Note that the value of the parameter is completely ignored - it's only the compile-time type which is important.

Personally I think this is pretty ugly, and I'd just use the constructor directly. If you change the type of ProtoTypes the compiler will spot the difference, and it won't take long at all to fix it up...

EDIT: Two alternatives to consider:

  • A similar method, but with an out parameter:

    public static class Lists
    {
        public static void NewList<T>(out List<T> list)
        {
            list = new List<T>();
        }
    }
    
    ...
    
    Lists.NewList(out ProtoTypes);
    
  • The same method, but as an extension method, with the name New:

    public static class Lists
    {
        public static List<T> New<T>(this List<T> list)
        {
            return new List<T>();
        }
    }
    
    ...
    
    ProtoTypes = ProtoTypes.New();
    

I prefer the first approach to either of these :)

Scrannel answered 18/7, 2013 at 18:43 Comment(5)
During the design of C# 3 we considered "mumble typing" of the form List<?> mylist = new List<int>(), because it makes certain scenarios involving anonymous types easier to write. Obviously it never got implemented.Hutchens
@EricLippert: Clearly the solution is to overload the unary + operator in List<T> to just return a new List<T>. Then the constructor body could just be: ProtoTypes = +ProtoTypes; After all, the unary plus operator doesn't have any other important uses for non-literal-representable types, right? ;)Scrannel
@HighCore: For which version - the static method, or the operator horribleness?Scrannel
@JonSkeet The static method could also be turned into an extension, but the operator overloading is something that I could never have imagined.Lira
@HighCore: I was originally thinking of the extension method, in fact - I've added that and an out parameter variant. Shame we can't add operators separately, otherwise we could use the operator. Then run away screaming.Scrannel
A
7

As Jon Skeet said and Eric Lippert backed up, constructors for generic classes in C# cannot infer their types from their parameters or the type of the variable to which the construction is assigned. The go-to pattern when this type of behavior is useful is usually a static generic factory method, which can infer its own generic type from those of its parameters. Tuple.Create() is an example; give it any list of parameters up to 8, and it will create a strongly-typed generic Tuple with those parameters as the data fields. This doesn't work out well for your case, however.

When the variable will be local, consider doing it the other way around; use variable type inference, via the var keyword:

var Prototypes = new List<double[][]>();

This is how the C# team decided to cut down on typing when instantiating variables. Locals are created - and change - much more often than instance variables, and this approach makes C# code look a little more like JavaScript.

As Jon showed, it's possible to hide the mess, but you'll create more of a mess in the process. Here's another possibility using .NET 3.5/4.0's Expression features:

public static string GetName(this Expression<Func<object>> expr)
{
    if (expr.Body.NodeType == ExpressionType.MemberAccess)
        return ((MemberExpression) expr.Body).Member.Name;

    //most value type lambdas will need this because creating the Expression
    //from the lambda adds a conversion step.
    if (expr.Body.NodeType == ExpressionType.Convert
            && ((UnaryExpression)expr.Body).Operand.NodeType 
                == ExpressionType.MemberAccess)
        return ((MemberExpression)((UnaryExpression)expr.Body).Operand)
                   .Member.Name;

    throw new ArgumentException(
        "Argument 'expr' must be of the form ()=>variableName.");
}

public static void InitializeNew(this object me, params Expression<Func<T>>[] exprs) 
    where T:new()
{
    var myType = me.GetType();
    foreach(var expr in exprs)
    {
       var memberName = expr.GetName()
       var myMember = myType.GetMember(memberName,
               BindingFlags.Instance|BindingFlags.Public
                   |BindingFlags.NonPublic|BindingFlags.FlattenHierarchy,
               MemberTypes.Field|MemberTypes.Property);

       if(myMember == null) 
           throw new InvalidOperationException(
               "Only property or field members are valid as expression parameters");

       //it'd be nice to put these under some umbrella of "DataMembers",
       //abstracting the GetValue/SetValue methods
       if(myMember.MemberType == MemberTypes.Field)
           ((FieldInfo)myMember).SetValue(me, new T());
       else
           ((PropertyInfo)myMember).SetValue(me, new T());
    }
}

//usage
class MyClass
{
    public List<double[][]> list1;
    public List<double[][]> list2;
    public MyOtherObject object1;

    public MyClass()
    {
       this.Initialize(()=>list1, ()=>list2);
       this.Initialize(()=>object1); //each call can only have parameters of one type
    }
}

The implication is obvious here; it's more trouble than it's worth.

To explain why I seemingly just had this laying around; the above is an adaptation of a method I use to throw ArgumentNullExceptions based on passed parameters, which requires the values to be encapsulated within Expressions in order to retain the names of the actual parameters from the calling method. In that situation, the complexity behind the scenes is reduced since all I need in the main helper is a check for null, and the added complexity saves me a lot more than I spend, by allowing me to one-line my null checks in every method and constructor of the codebase.

I recommend ReSharper as a long-term solution to reducing this typing. When the type of an assignment target is known (as it is for instance fields and properties), and you type = new, ReSharper will pop up a suggestion for the type of the constructor, and auto-fill it for you if you want. If you change either the type or constructor afterward, R# will flag the assignment as inconsistent, and you can tell R# to change whichever one you want to match the other.

Anglican answered 18/7, 2013 at 19:44 Comment(1)
There is so much stuff that is built in to IDEA / Android Studio and exists only in extensions for Visual Studio...Semicircle
W
3

If you just want to reduce code verbosity there is an opposite shortand syntax: the var operator

Old: List<int> intList = new List<int>();

New: var intList = new List<int>();

At least you write List only once

Winn answered 31/5, 2016 at 12:48 Comment(1)
Unfortunately, does not work if we want a different type than declared for encapsulation, i.e. on Properties. ISet<int> = new HashSet<int>(); being an example.Wylie
B
0

You can use new(). For example List<int> numbers = new(); //will create new List<int>()

Baylor answered 14/11, 2023 at 19:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.