3

Working with C# Generics you can have a class like this:

class Foo<T> where T:new() {}

Which means that the type T should have a constructor without parameters. It would be nice if we could have:

class Foo<T> where T : new(string)
{
    private T CreateItem()
    {
        string s="";
        return new T(s);
    }
}

Is there any reason that Microsoft haven't added this feature to the language?

2

3 Answers 3

11

Is there any reason that Microsoft haven't added this feature to the language?

The feature you describe is a specific case of the of the more general feature "allow a constraint that requires a particular method to exist". For example, you might say:

void M<T>(T t) where T has an accessible method with signature double Foo(int)
{
    double x = t.Foo(123);
}

We don't have that feature in C# because features have to be justified by a cost-benefit analysis. That would be a pretty expensive feature from both a design and implementation point of view -- a feature that would drive requirements onto not just C# but every .NET language. What's the compelling benefit that justifies the feature?

Moreover: suppose we did design that feature. How would it be implemented efficiently? The constraints in the generic type system have been carefully designed so that the jitter can generate efficient code once that can then be shared for every reference type. How would we generate efficient code for arbitrary method pattern matching? That sort of efficient dispatch is pretty straightforward when the method's slot can be known at compile time; with this feature we would no longer have that advantage.

The feature you want is the same feature, just with the kind of method restricted to a constructor.

Remember, the purpose of generics is to let you write generically typed code. If you're requiring constraints that are more specific than things that can be captured in the type system then you might be trying to abuse generics.

Sign up to request clarification or add additional context in comments.

9 Comments

if this is so hard, then why allow a parameterless constructor.
@Ariel: The answer to your question is straightforward: the cost of that much smaller feature is justified by the benefit it confers.
@Michael: That's the one gotcha. An interface works fine for the general problem--- except for the contructor. You can't put a .ctor into an interface.
To answer your question on how to implement it efficiently, one approach (taken by F#, which does allow such constraints) is to inline a type-specific version of the code at each call-site, losing the benefit of sharing.
@voroninp: If you have a method M<T>() and you call M<string> and M<Exception>, the jitter only generates the machine code once. Do you see why it can safely do so? All references are the same size, and all the virtual method calls are on the same slots. The only things you have to fix up dynamically are things like x is T and other type tests, but those are easily parameterized.
|
7

Rather than try to guess why Microsoft decided on a particular implementation, here's a workaround for you, using the factory pattern

public interface IFactory<T>
{
   T CreateItem(string s);
}

class Foo<TFactory,T> where TFactory : IFactory<T>, new()
{
    private T CreateItem()
    {
        var factory = new TFactory();
        string s="";
        return factory.CreateItem(s);
    }
}

Using this pattern, say you have a class Bar which has a constructor taking a single string:

public class Bar
{
   public Bar(string laa)
   {}
}

You just need a BarFactory which implements IFactory<Bar>

public class BarFactory : IFactory<Bar>
{
   public BarFactory () {}
   public Bar CreateItem(string s)
   {
      return new Bar(s);
   }
}

Now you can use that factory with Foo

var foo = new Foo<BarFactory,Bar>(); // calls to CreateItem() will construct a Bar

2 Comments

the factory pattern is a good patter and this is a good use of it, but this not the answer my question. Having the T:new() specification can't solve all scenarios. And when you have classes with multiple parameters it gets dirty. Usually i do something like this, and lately i was wondering why is this not part of the language? thats the question.
@Ariel - fair enough, but a) this might help someone else and b) there is no valid answer to your question. Plus c) This could be extended to provide any combination of parameters if a more specific name is chosen for IFactory.
1

The solution I adopted when I wanted a generic function that invoked a constructor with arguments was to use reflection to find and invoke it.

Given that I had control of both the generic, and all the classes it was implemented over (and this function was the only place those classes were constructed), I think I might have been better to give the classes a default constructor and added an Initialize method in the interface they all implemented.

(A solution that allowed adding static methods in general to an interface would be ideal.)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.