简体   繁体   中英

How to assign different generic classes to same variable

I know this question has probably been asked many times and my title probably needs some help so feel free to close this question after pointing me to a duplicate... just not sure how to properly search for the answer at the moment.

I've got a situation where a variable may need to hold and instance of ISolrOperations<Class1> or it may need to hold an instance of ISolrOperations<Class2> . The problem is that there is no base version of the interface and so there's no class I can define the variable as (other than object, which obviously has no knowledge of ISolrOperations<T> 's methods) to be able to hold either class.

So the only solutions I know of are either a) Do an if/else statement where I do everything I need with ISolrOperations<Class1> in one block and everything I need in ISolrOperations<Class2> in the other block even though each block will basically contain duplicate code. Or b) I could make the variable dynamic and just lose some compile time validation of my code.

I'm trying to think if there is some way I can create a generic wrapper class that has a non-generic base. And maybe the base has to treat it as an object but uses reflection... but them I'm like screw it; if I have to go through that trouble may as well just use the dynamic keyword.

Is there a more "proper" design pattern to handle this scenario? Or is my choice basically between dynamic or writing a lot of code just to make myself feel like I'm doing it "right" when dynamic is really the most practical solution?

Edit: Actually I'm not sure if dynamic would solve this problem :/

Edit 2: Nope... other code uses lambdas with the variable which requires casting it which I obviously don't know what to cast to at compile time so that won't work.

Although I am not sure, this approach might help. This approach uses adapter pattern .

public interface ISolrOperations<T> 
{
    SqlQuery<T> Execute();
}

public interface ISolrOperationsAdapter
{
    IEnumerable<Base> Execute();
}

//Idealy you have interfaces
public class Base { }
public class Class1 : Base { }
public class Class2 : Base { }  

public abstract class SolrOperationsAdapter : ISolrOperationsAdapter
{
    protected SolrOperationsAdapter()
    {
    }

    public IEnumerable<Base> Execute()
    {
        return ExecuteImpl();
    }

    protected abstract IEnumerable<Base> ExecuteImpl();
}


public class GenericSolrOperationsAdapter<T> : SolrOperationsAdapter
{
    private readonly ISolrOperations<T> _solrOperations;

    public static ISolrOperationsAdapter From(ISolrOperations<T> solrOperations)
    {
        return new GenericSolrOperationsAdapter<T>(solrOperations);
    }

    protected GenericSolrOperationsAdapter(ISolrOperations<T> solrOperations)
        : base()
    {
        _solrOperations = solrOperations;
    }

    //If you define interfaces you can return return IEnumerable<IInterface>
    protected override IEnumerable<Base> ExecuteImpl()
    {
        //here you can somehow convert result of query to your types(Class1, Class2 or some interface behind)
        return _solrOperations.Execute().ConvertTo<IEnumerable<Base>>();
    }
}

Usage example:

ISolrOperations<Class1> solrOperations = new SolrOperationsImplementation()
ISolrOperationsAdapter adapter = GenericSolrOperationsAdapter<Class1>.From(solrOperations);
IEnumerable<Base> result = adapter.Execute(); 

Wrap the class in a generic adapter class that implements the required interface.

This is called the adapter pattern in case you want to search for examples of it.

Example of a generic adapter:

public class SomeGenericInterfaceAdapter<T> : INonGenericInterface
{
    private IGenericInterface<T> _someGenericInterface;
    public SomeGenericInterfaceAdapter(IGenericInterface<T> someGenericInterface)
    {
        _someGenericInterface = someGenericInterface;
    }

    public void SomeMethod()
    {
        _someGenericInterface.SomeMethod();
    }
}

public interface INonGenericInterface
{
    void SomeMethod();
}

public interface IGenericInterface<T>
{
    void SomeMethod();
}

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