简体   繁体   中英

Returning an object derived from an interface with generic list

My application reads in JSON from disk and deserialising using JSON.net; which is working fine.

My JSON is laid out like this:

{
  "driver": {
    "driverTag": "blah_blah",
    "driverName": "Blah Blah",        
    "driverTransport": "serial-device"
  },
  "devices": [
    {
      "deviceName": "Dev1",
      "deviceTag": "DEV1",
      "deviceStartMode": "Auto"         
    },
    {
      "deviceName": "Dev2",
      "deviceTag": "DEV2",
      "deviceStartMode": "Auto"          
    }
  ]
}

Based on the "driverTransport" value, I deserialise to either a SerialDriverConfig, TelnetDriverConfig, SNMPDriverConfig... etc class.

As the "driver" properties will be the same for every driver, no matter the transport type, I have a "DriverConfigTemplate" class. The "devices" will differ from JSON file to JSON file and have specific properties for that transport type (ie a serial device will have properties like "serialPortName", "serialBaudRate" etc.)

I have a "DriverConfig" interface, where T is "DeviceConfig".

public interface DriverConfig<T> where T : DeviceConfig
{
    DriverConfigTemplate driver { get; set; }
    List<T> devices { get; set; }
}

My device config is as follows:

public class DeviceConfig : IDeviceConfig
{
    public string deviceTag { get; set; }
    public string deviceName { get; set; }
    public string deviceStartMode { get; set; }
}

Now; the problem part. When I am deserialising, I check the transport type before hand and determine the class to use; ie for a serial driver I will use the "SerialDriverConfig" class and deserialise using the "SerialDeviceConfig":

public class SerialDeviceConfig : DeviceConfig
{
    public int serialComPort { get; set; }
    public int serialBaudRate { get; set; }
    public int serialDataBits { get; set; }
    public string serialParity { get; set; }
    public string serialStopBits { get; set; }
    public string serialHandshake { get; set; }
    public int serialReadTimeout { get; set; }
    public int serialWriteTimeout { get; set; }
    public bool serialRtsEnable { get; set; }
    public bool serialDtrEnable { get; set; }
}

My "SerialDriverConfig" class looks like this:

public class SerialDriverConfig : DriverConfig<SerialDeviceConfig>
{
    public DriverConfigTemplate driver { get; set; }
    public List<SerialDeviceConfig> devices { get; set; }
}

Again, this is fine and the JSON.net deserialiser does its job perfectly.

I have a function that gets called when the JSON config file has been loaded and validated against its respective schema, then passed on to a "DeserialiseDriverConfig" function where I am trying to return the derived driver object; which is where I am stuck :(

 private DriverConfig<DeviceConfig> DeserialiseDriverConfig(string _json, string _driverTransport)
    {            
        switch (_driverTransport)
        {
            case "serial-device":
                try
                {
                    SerialDriverConfig _serialDriverConfig = JsonConvert.DeserializeObject<SerialDriverConfig>(_json);
                    if (_serialDriverConfig != null)
                    {                            
                        return _serialDriverConfig;
                    }
                }
                catch (Exception e)
                {
                    //Blah blah blah
                }

                break;
        }

        return null;
    }

I have been stuck on this one for a few days, have tried many things and this is where I have ended up. I am getting "Cannot implicitly convert type "SerialDriverConfig" to "DriverConfig". An explicit conversion exists (are you missing a cast?)" So I understand why this error is occurring, but cannot get around it.

Hope my code makes sense and someone can help me out here?

I'm not sure if this solution fits your need but if you create your method and SerialDriverConfig with using generic type T you can use your interface as a returning type. Can you try the code below;

Your Method:

private static DriverConfig<T> DeserialiseDriverConfig<T>(string _json, string _driverTransport)
{
    switch (_driverTransport)
    {
        case "serial-device":
            try
            {
                SerialDriverConfig<T> _serialDriverConfig = JsonConvert.DeserializeObject<SerialDriverConfig<T>>(_json);
                if (_serialDriverConfig != null)
                {
                    return _serialDriverConfig;
                }
            }
            catch (Exception e)
            {
                //Blah blah blah
            }

            break;
    }

    return null;
}

SerialDriverConfig Class:

public class SerialDriverConfig<T> : DriverConfig<T>
{
    public DriverConfigTemplate driver { get; set; }
    public List<T> devices { get; set; }
}

Also you should consider changing DriverConfig<T> interface approach because if you leave it as-is you will have boxing issue. If you do not need you may remove where T : DeviceConfig from your interface or modify it according to your current circumstances.

Hope this helps, please let me know if this works for you

You can change your DriverConfig class to be non-generic

public interface DriverConfig 
{
    DriverConfigTemplate driver { get; set; }
    List<DeviceConfig> devices { get; set; }
}

and instead of using derived classes ( SerialDriverConfig etc.) you can set Json.net to deserialize to the correct DeviceConfig type based on either having a $type attribute in your JSON like this or using a custom JsonConverter similar to this

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