Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

generic interfaces and polymorphism

I have following code:

public abstract class Operand<T>
{
    public T Value { get; protected set; }

    public bool IsEmpty { get; protected set; }

    public override string ToString()
    {
        return IsEmpty ? Value.ToString() : string.Empty;
    }
}
public class DoubleOperand : Operand<Double> {}

public interface IOperandFactory<T>
    {
        Operand<T> CreateEmptyOperand();
        Operand<T> CreateOperand(T value);
    }

public class DoubleFactory: IOperandFactory<double>
{
    public Operand<Double> CreateEmptyOperand()
    {
        //implementation
    }

    public Operand<Double> CreateOperand(double value)
    {
        //implementation
    }
}

I simlified code to just show the structure. Now I need associationDictionary that will return IOperandFactory for required Type: Something like this:

var factoryDict = 
new Dictionary<Type, IOperandFactory<>>() { { typeof(double), new DoubleFactory() } };

Could you help me to achieve it if it is possible?

like image 728
Danil Avatar asked Jan 16 '12 08:01

Danil


2 Answers

To do that, you would need to have a non-generic interface (typically in addition to the generic interface), i.e. a non-generic Operand, with Operand<T> : Operand (could also be an interface), and a non-generic IOperandFactory with IOperandFactory<T> : IOperandFactory. The only other option is to store a Dictionary<Type, object>, and have the caller cast as necessary.

Here's the non-generic approach:

using System.Collections.Generic;
using System;
public interface IOperand
{
    object Value { get; }
    bool IsEmpty { get; }
}
public abstract class Operand<T> : IOperand
{
    public T Value { get; protected set; }
    object IOperand.Value { get { return Value; } }
    public bool IsEmpty { get; protected set; }
    public override string ToString()
    {
        return IsEmpty ? Value.ToString() : string.Empty;
    }
}
public class DoubleOperand : Operand<double> { }

public interface IOperandFactory
{
    IOperand CreateEmptyOperand();
    IOperand CreateOperand(object value);
}
public interface IOperandFactory<T> : IOperandFactory
{
    new Operand<T> CreateEmptyOperand();
    Operand<T> CreateOperand(T value);
}

public class DoubleFactory : IOperandFactory<double>
{
    public Operand<double> CreateEmptyOperand()
    {
        throw new NotImplementedException();
    }
    IOperand IOperandFactory.CreateEmptyOperand() {
        return CreateEmptyOperand();
    }

    IOperand IOperandFactory.CreateOperand(object value) {
        return CreateOperand((double)value);
    }
    public Operand<double> CreateOperand(double value)
    {
        throw new NotImplementedException();
    }
}

static class Program
{
    static void Main()
    {
        var factoryDict = new Dictionary<Type, IOperandFactory> {
          {typeof (double), new DoubleFactory()}
        };
    }
}
like image 73
Marc Gravell Avatar answered Nov 13 '22 22:11

Marc Gravell


If I understand correctly, you are trying to store a collection of generic types, where the generic type parameters may vary. If this is the case, it is not directly possible, as the following example illustrates:

// You have lists of different types:
List<double> doubleCollection = new List<double>();
List<string> stringCollection = new List<string>();

// Now to store generically:
var collection = new List<List< /* ... Which type parameter to use? ... */ >>();

What should be apparent here, is that it is not possible to deduce which type parameter to use. Instead (with regards to your example), you may want something like this instead:

public interface IOperand
{
}

public interface IOperand<T>
{
}

public interface IOperandFactory
{
    IOperand CreateEmptyOperand();
    IOperand CreateOperand(object value);
}

public interface IOperandFactory<T> : IOperandFactory
{
    new IOperand<T> CreateEmptyOperand();
    IOperand<T> CreateOperand(T value);
}

public class DoubleFactory : IOperandFactory<double>
{
    public IOperand<double> CreateEmptyOperand()
    {
        throw new NotImplementedException();
    }

    public IOperand<double> CreateOperand(double value)
    {
        throw new NotImplementedException();
    }

    IOperand IOperandFactory.CreateEmptyOperand()
    {
        throw new NotImplementedException();
    }

    public IOperand CreateOperand(object value)
    {
        throw new NotImplementedException();
    }
}

public class SomeContainer
{
    public SomeContainer()
    {
        var factoryDict = new Dictionary<Type, IOperandFactory>()
        {
            { typeof(double), (IOperandFactory)new DoubleFactory() }
        };
    }
}

This may not be the most elegant of solutions, but it would allow you to store different generic types in the same collection. An issue with this, however, is that the caller accessing such a collection would need to know what type to cast to. For example:

// ... Inside SomeContainer ...
public IOperandFactory<T> GetFactory<T>()
{
    return (IOperandFactory<T>)factoryDict[typeof(T)];
}

So with this, you can get the DoubleFactory using:

IOperandFactory<double> doubleFactory = mSomeContainerInstance.GetFactory<double>();
IOperand<double> emptyOperand = doubleFactory.CreateEmptyOperand();
IOperand<double> filledOperand = doubleFactory.CreateOperand(1.0d);
like image 35
Samuel Slade Avatar answered Nov 13 '22 21:11

Samuel Slade