Jan Kowalski
Jan Kowalski

Reputation: 45

Is there a possibility to somehow overcome limitations of using generics in interface? (without boxing/unboxing)

I'm trying to create set of interfaces that will be used in several classes. But I have troubles with setting them properly. I want to keep them generic.

I tried to get around this using dynamic type and object type without success. Maybe this will be clear with attached those interfaces below:

In code below type T is unknown. I cannot specify it because each IDeviceParameter will be different type. And number of parameters is not fixed, so I want to simply add all available IParameters to list. Only solution that came into my mind is to use some sort of boxing/unboxing (for example cast int to object and vice versa) or use dynamic. Or to totally change those interfaces. Thanks you in advance for any advises.

public interface IDevice : IDisposable
{
    string Name { get; }
    bool Close();
    Guid DeviceGuid { get; }
    IList<IDeviceParameter<T>>AvailableParameters { get; }
    IList<IDeviceCommand> AvailableCommands { get; }
}



public interface IDeviceParameter<T>
{
    event EventHandler<IDeviceParameter<T>> ParameterValueChanged;
    event EventHandler<Exception> ParameterNotSet;
    string ParameterName { get; }
    string ParameterValue { get; }
    string ParameterUnit { get; }
    bool IsReadOnly { get; }
    T Parameter { get; }
    void SetParameter(T value);
}

Below is some concrete implementation (not working because of error like "Cannot convert from type Object to type int") :

public class Device : IDevice
{
    public int _setableParameter1 = 1;
    public string _setableParameter2 = "2";
    private Guid _guid;
    private string _name;
    private List<IDeviceCommand> _cmdList = new List<IDeviceCommand>();
    private List<IDeviceParameter<object>> _paramList = new List<IDeviceParameter<object>>();

   public Device()
   {
        IDeviceParameter<int> setableParameter1 = DeviceParameterFactory.Factory<int>.CreateParameter("SomeParameter1", "V", delegate { throw new DeviceParameterFactory.ParametersNotSetException("SomeParameter1"); },  ref _setableParameter1);
         IDeviceParameter<string> setableParameter2 = DeviceParameterFactory.Factory<string>.CreateParameter("SomeParameter2", "V", delegate { throw new DeviceParameterFactory.ParametersNotSetException("SomeParameter2"); },  ref _setableParameter2);
        _cmdList.Add(setableParameter1);
        _cmdList.Add(setableParameter2);
        _guid = Guid.NewGuid();
        _name = "Device" + DateTime.Now.Millisecond;
    }

Some explanation: factory class returns IDeviceParameter, and those types for sure are known at compilation time because they are explictly set.

Upvotes: 0

Views: 73

Answers (1)

Max Meijer
Max Meijer

Reputation: 1729

If you have something of type IDeviceParameter<string> and something of IDeviceParameter<int> then there is no way to make that into an IDeviceParameter<object> without doing new IDeviceParameter<object>() somewhere. This is because they are just entirely different interfaces to the program. For the compiler you might as well have created classes named IIntDeviceParameter and IStringDeviceParameter.

If these classes share a similar interface, then you will need to explicitly state this, i.e. you would need to make an interface IUnknownTypeDeviceParameter, but you cannot put the fields or methods that use T in their type definition in there. So you could do this:

public interface IUnknownTypeDeviceParameter
{
    event EventHandler<Exception> ParameterNotSet;
    string ParameterName { get; }
    string ParameterValue { get; }
    string ParameterUnit { get; }
    bool IsReadOnly { get; }
}

public interface IDeviceParameter<T> : IUnknownTypeDeviceParameter
{
    event EventHandler<IDeviceParameter<T>> ParameterValueChanged;
    event EventHandler<Exception> ParameterNotSet;
    string ParameterName { get; }
    string ParameterValue { get; }
    string ParameterUnit { get; }
    bool IsReadOnly { get; }
    T Parameter { get; }
    void SetParameter(T value);
}

If you would like to use one of the functions in IDeviceParameter<T> on an IUnknownTypeDeviceParameter you would need to cast it back to an IDeviceParameter<T>.

Upvotes: 2

Related Questions