tags:

views:

5174

answers:

7

I'm trying to create a new object of type T via its constructor when adding to the list.

I'm getting a compile error: The correct error message is: 'T': cannot provide arguments when creating an instance of a variable

But it does! Any ideas?

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       tabListItems.Add(new T(listItem)); // error here.
   } 
   ...
}
+5  A: 

You need to add where T: new() to let the compiler know that T is guaranteed to provide a default constructor.

public static string GetAllItems<T>(...) where T: new()
Richard
UPDATE: The correct error message is: 'T': cannot provide arguments when creating an instance of a variable
LB
That's because you're not using a blank constructor, you're passing an argument to it of object. There's no way it can handle that without specifying that the generic Type has a new(object) parameter.
Min
Then you'll need to either:1. Use reflection2. Pass the parameter into an initialization method instead of the constructor, where the initialization method belongs to an interface that your type implements and which is included in the where T: ... declaration.Option 1 is the lowest impact for the rest of your code, but option 2 provides compile time checking.
Richard
Don't use reflection! There are other ways as outlined in other answers that get you the same effect.
Garry Shutler
@Garry - I'd agree that reflection isn't necessarily the best approach, but it does allow you to achieve what's required with minimal change to the rest of the code base. That said, I do much prefer the factory delegate approach from @JaredPar.
Richard
+2  A: 

By the "new() constraint", the compiler means that when you declare the class (or method) with the generic parameter, you need to specify that it has an empty constructor. You can do this like so, for a class:

public class MyClass<T> where T : new()
{

}
impulse3d
I think the question was to pass parameter to new!
Thunder
A: 

I believe you have to constraint T with a where statement to only allow objects with a new constructor.

RIght now it accepts anything including objects without it.

peiklk
+40  A: 

In order to create an instance of a generic type in a function you must constrain it with the "new" flag.

public static string GetAllItems<T>(...) where T : new()

However that will only work when you want to call the constructor which has no parameters. Not the case here. Instead you'll have to provide another parameter which allows for the creation of object based on parameters. The easiest is a function.

public static string GetAllItems<T>(..., Func<ListItem,T> del) {
  ...
  List<T> tabListItems = new List<T>();
  foreach (ListItem listItem in listCollection) 
  {
    tabListItems.Add(del(listItem));
  }
  ...
}

You can then call it like so

GetAllItems<Foo>(..., l => new Foo(l));
JaredPar
+1 nice work around for the constructor limitations. Be nice if future version would support parameterized constructor constraints
JoshBerke
Sneaky, I like it.
Garry Shutler
+1. Annoying, but better than nothing.
Richard Berg
How would this work when called internally from a generic class? I have posted my code in an answer below.I don't know the concrete class internally, as it's a generic class.Is there a way round this.I dont want to use the other suggestion of using property initialiser syntax as that will bypass the logic I have in the constructor
Christo Fur
added my code to another questionhttp://stackoverflow.com/questions/1682310/c-generics-problem-newing-up-the-generic-type-with-parameters-in-the-construct
Christo Fur
This is currently one of the most annoying limitations of C#. I would like to make my classes immutable: Having just private setters would make the class impossible to be in an invalid state by side effects. I also like to use that Func and lambda, but I know it still is a problem in bussiness world as generally programmers don't know lambdas yet and this makes your class harder to understand.
Tuomas Hietanen
+3  A: 

This will not work in your situation. You can only specify the constraint that it has an empty constructor:

public static string GetAllItems<T>(...) where T: new()

What you could do is use property injection by defining this interface:

public interface ITakesAListItem
{
   ListItem Item { set; }
}

Then you could alter your method to be this:

public static string GetAllItems<T>(...) where T : ITakesAListItem, new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       tabListItems.Add(new T() { Item = listItem });
   } 
   ...
}

The other alternative is the Func method described by JaredPar.

Garry Shutler
this would bypass any logic that is in the constructor that takes the arguments though, right?I would like to do something Like Jared's approach but am calling the method internally within the class so don't know what the concrete type is...hmmm
Christo Fur
Right, this calls the logic of the T() default constructor, then simply sets the property "Item". If you're trying to invoke the logic of a non-default constructor, this will not help you.
Scott Stafford
+1  A: 

If you simply want to initialise a member field or property with the constructor parameter, in C# >= 3 you can do it very easier:

public static string GetAllItems<T>(...) where T : InterfaceOrBaseClass, new() 
{ 
   ... 
   List<T> tabListItems = new List<T>(); 
   foreach (ListItem listItem in listCollection)  
   { 
       tabListItems.Add(new T{ BaseMemberItem = listItem }); // No error, BaseMemberItem owns to InterfaceOrBaseClass. 
   }  
   ... 
} 

This is the same thing Garry Shutler said, but I'd like to put an aditional note.

Of course you can use a property trick to do more stuff than just setting a field value. A property "set()" can trigger any processing needed to setup its related fields and any other need for the object itself, including a check to see if a full initialization is to take place before the object is used, simulating a full contruction (yes, it is an ugly workaround, but it overcomes M$'s new() limitation).

I can't be assure if it is a planned hole or an accidental side effect, but it works.

It is very funny how M$ people adds new features to the language and seems to not do a full side effects analysis. The entire generic thing is a good evidence of this...

fljx
Both constraints are needed.InterfaceOrBaseClass makes the compiler aware of the field/property BaseMemberItem.If the "new()" constraint is commented, it will trigger the error:Error 6 Cannot create an instance of the variable type 'T' because it does not have the new() constraint
fljx
A situation I encountered wasn't exactly like the question being asked here, however this answer got me where I needed to go and it seems to work very well.
digitall
+3  A: 

Since nobody bothered to post the 'Reflection' answer (which I personally think is the best answer), here goes:

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       Type classType = typeof(T);
       ConstructorInfo classConstructor = classType.GetConstructor(new Type[] { listItem.GetType() });
       T classInstance = (T)classConstructor.Invoke(new object[] { listItem });

       tabListItems.Add(classInstance);
   } 
   ...
}
James Jones