views:

62

answers:

4

Is it possible to construct an object with its internal constructor within a generic method?

public abstract class FooBase { }

public class Foo : FooBase {
   internal Foo() { }
}

public static class FooFactory {
    public static TFooResult CreateFoo<TFooResult>()
    where TFooResult : FooBase, new() {
        return new TFooResult();
    }
}

FooFactory resides in the same assembly as Foo. Classes call the factory method like this:

var foo = FooFactory.CreateFoo<Foo>();

They get the compile-time error:

'Foo' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TFooType' in the generic type or method 'FooFactory.CreateFoo()'

Is there any way to get around this?

I also tried:

Activator.CreateInstance<TFooResult>(); 

This raises the same error at runtime.

A: 

The type argument must have a public parameterless constructor. When used together with other constraints, the new() constraint must be specified last.

http://msdn.microsoft.com/en-us/library/d5x73970.aspx

edit: so no, if you use new() constraint, you cannot pass that class, if you don't use new() constraint you can try using reflection to create new instance

public static TFooResult CreateFoo<TFooResult>()
where TFooResult : FooBase//, new()
        {
            return (TFooResult)typeof(TFooResult).GetConstructor(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null, new Type[] {}, null).Invoke(new object[]{});
            //return new TFooResult();
        }
Kikaimaru
Yes, thank you. I am aware of that....."Is there any way to get around this?"
David Neale
+1  A: 

You could remove the new() constraint and return:

//uses overload with non-public set to true
(TFooResult) Activator.CreateInstance(typeof(TFooResult), true); 

although the client could do that too. This, however, is prone to runtime errors.

This is a hard problem to solve in a safe manner since the language does not permit an abstract constructor declaraton.

Ani
I tried that - the same error is just thrown at runtime instead of when compiling.
David Neale
@David Neale: Calling the `nonPublic` overload will handle that. Updated.
Ani
Excellent - I didn't see that overload. Thanks.
David Neale
A: 

There can be few work-arounds as below but I don't think you want to go that way!

  • Put switch statement inside Factory that will create the instance based on type of type parameter.

  • Each concrete implementation of FooBase will register with FooFactory passing the factory method to create it self. So FooFactory will use the internal dictionary

  • Extending on the similar line except mapping between type parameter and concrete implementation would be external code (xml file, configuration etc). IOC/DI containers can also help here.

VinayC
A: 

Upgrade to .NET 4.

Paul Ruane
Have you come across any documentation supporting changes to generic constructors in C# 4.0?
David Neale
No, afraid not. The documentation still suggests it must be public, however the compiler disagrees. I haven't looked at the spec, however. (My original answer was in jest btw.)
Paul Ruane