shinzou
shinzou

Reputation: 6222

Instantiating a generic field of a class

Is there a way to have a generic field in a class to specialize to a specific type in the constructor?

For example:

class concreteClass1
{
    private int a;
    public concreteClass1( int a)
    {
        this.a = a;
    }
}

class concreteClass2
{
    string b;
    public concreteClass2(string b)
    {
        this.b = b;
    }
}

class A<T>
{
    private T field;
    public A(int x)
    {
        field = new concreteClass1(x); //error here CS0029
    }

    public A(string y)
    {
        field = new concreteClass2(y); //error here CS0029
    }
}

So T can be either concreteClass1 or concreteClass1 and their respective ctors will have different signatures.

Upvotes: 2

Views: 105

Answers (2)

Scott Hannen
Scott Hannen

Reputation: 29252

I would refactor this to use dependency injection. That way the class doesn't contain code to create other classes that it depends on, like myConcreteField = new ConcreteA<T>(4);. Dependency injection is used to keep code from getting tied into difficult knots like this.

(Your example is very, very abstract, which makes it a little difficult. If you use class names like "Concrete" and "Implementation" then it makes the answer harder to read because we use those same words to describe concepts.)

Instead, whatever that Concrete thing is, declare an interface, like

public interface ISomethingThatTheOtherClassNeeds<T>
{
    public int MySomething {get;set;}
}

public class SomethingThatTheOtherClassNeeds : ISomethingThatTheOtherClassNeeds<string>
{
    public int MySomething {get;set;}
}

Then in your Implementation class:

class Implementation<T>
{
    private readonly ISomethingThatTheOtherClassNeeds<T> _something;

    public Implementation(ISomethingThatTheOtherClassNeeds<T> something)
    {
        _something = something;
    }

    void DoSomething()
    {
        Console.Write(_something.MySomething.ToString());
    }
}

The difference is that instead of being responsible for creating whatever that class is, it's passed to Implementation in the constructor. Implementation doesn't even know what the class is - it just knows that it matches the interface.

This is especially helpful if those other classes in turn depend on more classes. If you're creating them by calling new in your class then that class has to know how to create those classes.

Then to wire it up you would use a dependency injection container like Windsor, Unity, Autofac, and many more. That's not very commonly done with console applications, but I'm guessing this is more experimental than real.

Upvotes: 1

Gaspa79
Gaspa79

Reputation: 5606

Well this was a bit tricky due to having to convert types. Maybe this will work for you?

class Program
{
    static void Main(string[] args)
    {
        var myImplementation = new Implementation<int>(4);
        var myImplementation2 = new Implementation<string>("Hello World");

        Console.WriteLine(myImplementation.myConcreteField); // outputs 4!
        Console.WriteLine(myImplementation2.myConcreteField); // outputs Hello World
    }
}

abstract class MyAbstract<T>
{
    public T MySomething;
    public MyAbstract(T something)
    {
        MySomething = something;
    }
}

class ConcreteA<T> : MyAbstract<T>
{
    public ConcreteA(int something) : base((T)Convert.ChangeType(something, typeof(T)))
    {
    }
}

class ConcreteB<T> : MyAbstract<T>
{
    public ConcreteB(string something) : base((T)Convert.ChangeType(something, typeof(T)))
    {
    }
}

class Implementation<T>
{
    public MyAbstract<T> myConcreteField;

    public Implementation(T a)
    {
        myConcreteField = new ConcreteA<T>(4);
    }

    void DoSomething()
    {
        Console.Write(myConcreteField.MySomething.ToString());
    }
}

Upvotes: 1

Related Questions