Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refactoring with generics

Tags:

c#

generics

Let's assume, that we have the following classes:

class ViewModelA
{
    private ProxyA proxy;

    public ViewModelA(DataA data)
    {
        proxy = new ProxyA(data);
    }

    public void DoSth()
    {
        proxy.DoSth();
    }

    public ProxyA Proxy
    {
        get
        {
            return proxy;
        }
    }
}

class ViewModelB
{
    private ProxyB proxy;

    public ViewModelB(DataB data)
    {
        proxy = new ProxyB(data);
    }

    public void DoSth()
    {
        proxy.DoSth();
    }

    public ProxyB Proxy
    {
        get
        {
            return proxy;
        }
    }
}

...

All of these classes are actually a lot longer, but also similar in the same degree.

Is there a (nice) way of converting them all to one generic class? The core problem is the line:

proxy = new ProxyA(data);

because C# disallows calling parametrized ctors on generic class specializations (in terms of T).

like image 791
Spook Avatar asked Oct 02 '22 15:10

Spook


2 Answers

You can solve this by passing in a preconstructed ProxyA or ProxyB object.

One way of doing it is through inheritance, constructing the object just before calling the base constructor:

class ViewModel<TProxy, TData>
{
    private TProxy proxy;

    public ViewModel(TProxy proxy)
    {
        this.proxy = proxy;
    }

    public void DoSth()
    {
        proxy.DoSth();
    }

    public TProxy Proxy
    {
        get
        {
            return proxy;
        }
    }
}

class ViewModelA : ViewModel<ProxyA, DataA>
{
    public ViewModelA(DataA data) : base(new ProxyA(data))
    {
    }
}

I've assumed here that DataA and DataB occurs somewhere in method signatures that you have omitted. If not, you can leave out the TData generic type parameter.

Reflection is always an option, but it is best avoided if a solution like this is acceptable. If you want to avoid having to create a new type for a different TProxy and TData, you could add a second constructor instead:

ViewModel(TData data, Func<TData, TProxy> factory) : this(factory(data))
{
}

Used as such:

new ViewModel<ProxyA, DataA>(new DataA(), data => new ProxyA(data));

I'm not sure if that makes sense to do in your application however.

like image 191
Thorarin Avatar answered Oct 05 '22 11:10

Thorarin


If you know the type (and using generics you would), you can call Activator.CreateInstance to create an instance of that type using any constructor you want.

proxy = Activator.CreateInstance(typeof(TProxy), new[]{data});

While this will solve your direct problem, you could also think about a design where ViewModelA does not know about DataA and instead gets passed a ProxyA. That would solve all your problems without fancy reflection.

like image 31
nvoigt Avatar answered Oct 05 '22 11:10

nvoigt