tags:

views:

280

answers:

7

I'm writing a Generic class as following.

public class Foo<T> : 
    where T : Bar, new()
{
    public void MethodInFoo()
    {
        T _t = new T();
    }
}

As you can see the object(_t) of type T is instantiated at run-time. To support instantiation of generic type T, language forces me to put new() in the class signature. I'd agree to this if Bar is an abstract class but why does it need to be so if Bar standard non-abstract class with public parameter-less constructor.

compiler prompts following message if new() is not found.

Cannot create an instance of the variable type 'T' because it does not have the new() constraint

+14  A: 

Because there isnt normally an assumption that the template parameter needs to be [non-abstract and] constructible [via a public parameterless constructor] in order for a Type to match the template parameter definition.

Until you add a :new() constraint on the template:

  • The compiler won't let you construct a T
  • The compiler will let you match T with abstract types or types without a public parameterless constructor

The :Bar bit is orthogonal and means:

  • Dont let people match against types that arent derived from [or are] Bar
  • Let me cast Ts to Bar or types derived from Bar within the body
  • let me call public and in-scope internal methods of Bar on a T
Ruben Bartelink
I don't like the wording "default constructor", as it may be unclear is some circumstances. I'd phrase it more like:Adding `, new()` forces user code to use a generic type argument that features a *no-argument* constructor.
Romain
@Romain: MSDN refers to it as a "public parameterless constructor". http://msdn.microsoft.com/en-us/library/d5x73970.aspx
LukeH
Corrected the question! I never noticed this. Thanks for correcting.
this. __curious_geek
When I studied, we called also that a `default constructor`. Seems right to me.
Kobi
@Luke, Romain: Both good points, included in answer.
Ruben Bartelink
@Kobi: default constructor is slightly wider definition which could be considered to include constructors with default values on all their parameters etc. Also, things like conversion operators start getting considered (in the heads of readers). One key missing bit from my initial answer was the fact that it had to be `public`.
Ruben Bartelink
I Agree with Ruben. calling it public parameter-less contructor conveys the right meaning of constructor being both public scope and 0 parameter signature.
this. __curious_geek
+0 The content of the answer is technically correct but fails to actually answer the "why" part of the question apart from effectively saying "you cant' because thats not the way it works". Yes but why does it work that way?
AnthonyWJones
@AnthonyWJones: 1. You're right that I didnt take to heart the final 'actual question' bit of the question. 2. I think it is important to explain that there are two things at work here a) the type hierarchy constraint b) the 'can construct' constraint. You're specifying them for different reasons. However, I'd like the first sentence to capture the main points *without any misleading information* and then let the rest be just extra information to aid the understaning. Can you suggest/execute an appropriate edit?
Ruben Bartelink
+2  A: 

Maybe because if you don't include the new() constraint then T could legitimately be a subclass of Bar with no default (ie, public and parameterless) constructor, in which case the new T() statement inside the method would be invalid.

  1. With only Bar as a constraint, T can be Bar or any derivative of Bar, with or without a default constructor.
  2. With only new() as a constraint, T can be any type with a default constructor.
  3. With Bar and new() as constraints, T must be Bar or a subclass of Bar and must also have a default constructor.
LukeH
@Ruben: Exactly!. Types derived from `Bar` don't necessarily have a default constructor, even if `Bar` itself does, hence the need for the `new()` constraint too.
LukeH
+1: Nice enumeration of the possibilities; No loose, potientially misleading info (and important other comments on my answer)
Ruben Bartelink
+1  A: 

Although Bar may be concrete, the derived class T could itself be abstract or lack a default constructor.

GraemeF
I'd say *may be* instead of *is*. The template spec doesnt care either way until you say `new()` but wont let you `new()` in the body
Ruben Bartelink
In my answer s/template/generic method/ :D Also s/the derived/a derived/. s/default constructor/public parmaeterless constructor/
Ruben Bartelink
+2  A: 

Because subclasses of Bar might not have an no-arg constructor.

where T : Bar

indicates Bar, or a subclass of Bar. If you just wanted an instance of Bar, you wouldn't use generics. There are plenty of instances (Object and String, for example) where the superclass has a no-arg constructor, and a subclass does not.

Sean Reilly
As with the other answers, until you say :new(), T doesnt have to have a [public parameterless] constructor (and the body cant try to use it]
Ruben Bartelink
+8  A: 

Just because the Bar class defines a parameter-less constructor, doesn't mean that everything that is a Bar will do so - there may be a class that inherits from Bar but hides the parameter-less constructor. Such a class would meet the Bar constraint but rightly fail the new() constraint.

(Note that if you make Bar sealed to avoid this possibility, you can (understandably) no longer use it as a generic constraint) - edit attempting this produces compiler error CS0701.

AakashM
While that's true, the compiler doesnt care whether even Bar has a public parameterless constructor [or let you use it in the body] until you say: `:new()`
Ruben Bartelink
+1. Simple, straightforward, correct and answers the "Why" in the question.
AnthonyWJones
You sure about the sealed precluding it matching a template spec and actually generating a compiler error? (I can imagine the compiler figuring that out and there not being much point if its sealed, but does it actually complain)?
Ruben Bartelink
@AnthonyWJones: Amusingly enough, if you go through the history on my answer, you'll see mine started in that spirit until I added stuff to catch all the other points being made. My comment on this answer reflects the fact that its slightly misleading to talk about the Bar having a default constructor when the compiler assumes no such thing until you tell it to, and wont let you construct even a Bar()
Ruben Bartelink
@Ruben Bartelink: yes compiler error CS0701, edited to add.
AakashM
AakashM: Nice, +1 for that point [Though I'm still looking forward to AnthonyWJones justifying this being a complete answer and mine having loads of redundant fluff that should just be stripped out :P]
Ruben Bartelink
+1  A: 

You could probably have use the Bar constructor:

T _t = new Bar();

without having the new() constraint. However, you used the T constructor and the compiler can not and does not assume that constructing the type that gets bound to T is possible until you add a new() constraint.

GoodEnough
+1 Good point - I'd add "and the compiler can not and does not assume that constructing the type that gets bound to T is possible until you add a `new()` constraint. If I was the questioner, I'd then torn between this and mine as the Accepted answer :P
Ruben Bartelink
Good suggestion, thanks
GoodEnough
A: 

For those not sure, remember you can use the

where T : IDeviceCommand

to require that T implements some interface as a minimum contractual requirement. Your could verbalize the above as "You can call me if the supplied Type 'T' at a minimum, implements the IDeviceCommand interface". This of course, allows you to make a series of (correct) assumptions of what facilities that 'T' provides your method to operate on.

TheEruditeTroglodyte