简体   繁体   中英

Using C# Generics to create a generic object from a method (not “new”)

I'm used to C++ templates and realize that things work a little differently in C#. Here's what I want:

    T CreateButton<T>() {
       T test = T.create(...some vars...);
       return test;
    }

I thought maybe using a derivative constraint with a base class that has the "create" method defined would do the trick but it still won't compile.

I get this compile error: **error CS0119: Expression denotes a 'type parameter', where a 'variable', 'value' or 'type' was expected**

Is there a way to accomplish what I'm trying to do in C#?

Your problem is that you're calling T.Create as if it were a static method on the generic T. That poses two problems - first, you cannot inherit statics (which you'd have to do to restrict the type of T to a base-class that defined 'static Create' so that T.Create would compile). Second, even if you could inherit a static, somehow the base class .Create() would have to 'know' to return a T.

What you're after here is a Factory. Define a class that acts as a factory for T's, Then you can write

T test = Factory < T >.Create(... some vars ...);

This feels like it would result in a giant switch statement of sorts - based on the real type of T do the right thing. But this is where inversion-of-control and dependency-injection can help you. Define a Factory with 'plug-ins' for each type of T you need. Use IoC to inject the plug-ins into your factory.

Check out the discussion here

This should be what you're after:

class MyFactory<T> where T : new()
{
    public T CreateMyStuff()
    {
        return new T();
    }
}

You have to define and implement an interface (or a base class). You will also have to constrain for new() . Then you can use generic constraints on it.

You can't call static methods against a generic parameter. IE: You can't call a static method on T .

The issue is with the specific line T.Create(...)

T is a Type, not a variable, so unless the Create() method is a static method of the type, then it wont compile, and even then T doesn't know what create actually is, so you wont be able to compile the function call to begin with. If you constrain T in some manner so that the code knows that the Create() function exists, then you can do this.

Example

Assume T will always be a member or inherited member of some Base class, the function would be declared as follows:

T CreateButton<T>() where T : BusinessObjectBase
{
   T test = (T)BusinessObjectBase.create(...some vars...);
   return test;
}

In this case, the static function Create() is declared inside the BusinessObjectBase, and the type passed in as T is constrained to be, or be extended from, that class, guaranteeing the code that T will be able to call the Create() function.

Of course, as others have mentioned, its far easier to use the new() constraint. This allows you to simply return new T(); far less complex, but you lose whatever those parameters were from the create function.

With the exception of types which satisfy a new constraint, there is no way to create a new object of generic type, given nothing but the type. Instead, your best bet is probably to have the method which needs to create the things accept a delegate suitable for the task.

// Suppose you need to be able to create things whose constructors should take
//  an Int32.  Then do something like:

void MakeLotsOfTs<T>(Func<Int32, T> CreationProc) // And whatever other stuff you want
{
  ... whenever you need a new T for a given integer N, call CreationProc(N)
}

If you have a class Foo whose constructor takes an Int32 , and you wish to pass it to the above method, use

  MakeLotsOfTs<Foo>( (Int32 param) => new Foo(param) );

Note that this approach will work even if you want to use it with a class whose constructor requires something else (e.g. one could do something like:

MakeLotsOfTs<Bar>( (Int32 param) => new Bar(String.Format("Bar #{0}", param)) );

This approach requires a little more work for the caller than would being able to specify a "parameterized" new constraint, but is much more powerful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM