简体   繁体   中英

Generic types, Interfaces

I would like to create a generic notification engine. The idea is to have a single core engine to process any type of notification. This engine will process notification and handle all logging, error handling etc..

I created 3 simple interfaces:

public interface INotificationInput
{
    /// <summary>
    /// Friendly Name for logging/tracing usage
    /// </summary>
    string FriendlyName { get; set; }

    string NotificationCode{ get; set; }

    Double Version { get; set; } 
}

public  interface INotificationOutput
{
    /// <summary>
    /// Friendly Name for logging/tracing usage
    /// </summary>
    string FriendlyName { get; }
}

public interface INotificationProvider<out Toutput, Tinput> where Toutput : INotificationOutput where Tinput : INotificationInput
{
    /// <summary>
    /// Friendly Name for logging/tracing usage
    /// </summary>
    string FriendlyName { get; set; }

    /// <summary>
    /// Generates and returns an INotificationOutput from data
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    Toutput GenerateNotificationOutput(Tinput data);
}

So the INotificationProvider will chunk the INotificationInput to create a INotificationOutput.

That could be information to send a email, a sms, you name it, the engine will call the methods and do the magic of scheduling, logging, handling errors and so on..

I implemented the interface like this:

/// <summary>
    /// INotificationInput represented by a dummy object
    /// </summary>
    public class DummyNotificationInput : INotificationInput
    {
        public string FriendlyName { get; set; }
        public string NotificationCode { get; set; }
        public double Version { get; set; }
    }

public class DummyNotificationOutput : INotificationOutput
{
    public string FriendlyName { get; private set; }
}

public class DummyProvider : INotificationProvider<DummyNotificationOutput, DummyNotificationInput>
{
    public string FriendlyName { get; set; }

    public DummyNotificationOutput GenerateNotificationOutput(DummyNotificationInput data)
    {
        throw new NotImplementedException();
    }
}

Now I would like my engine to have a list of provider:

 var providersList = new List<INotificationProvider<INotificationOutput, INotificationInput>>();

The problem is that I cannot to the following:

providersList.Add(new DummyProvider<DummyNotificationOutput, DummyNotificationInput>());

There must be a solution. Am I using the wrong approach?

The second generic type argument to INotificationProvider isn't covariant (at a conceptual level), but you're trying to use it as if it were. It is actually contravariant.

In your list of INotificationProvider objects you've defined the input notification as an INotificationInput . This means objects added to this list need to be able to accept any type of INotificationInput as input to their GenerateNotificationOutput function . You're trying to add an object that only knows how to handle DummyNotificationInput objects. It would fail if it were passed some other type of input.

Either your provider needs to accept INotificationInput objects, if you want to be able to add it to that list, or the list needs to define all of the objects as accepting DummyNotificationInput .

As Servy has already answered, you can't really do this due to what you providersList is expecting

With this in mind, it may actually be simpler to just make INotificationProvider non-generic:

public interface INotificationProvider
{
    /// <summary>
    /// Friendly Name for logging/tracing usage
    /// </summary>
    string FriendlyName { get; set; }

    /// <summary>
    /// Generates and returns an INotificationOutput from data
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    INotificationOutput GenerateNotificationOutput(INotificationInput data);
}

Then the DummyProvider becomes:

public class DummyProvider : INotificationProvider
{
    public string FriendlyName { get; set; }

    public INotificationOutput GenerateNotificationOutput(INotificationInput data)
    {
        throw new NotImplementedException();
    }
}

Now, probably not what you had in mind - you are expecting to pass DummyNotificationInput instances to DummyProvider

You could just type check in your Provider code

public class DummyProvider : INotificationProvider
{
    public string FriendlyName { get; set; }

    public INotificationOutput GenerateNotificationOutput(INotificationInput data)
    {
        if (!(data is DummyNotificationInput)) throw new ArgumentException("Invalid type specified", "data");

        return something...;
    }
}

Obviously, you lose design time checking - but if you really need to put them in a covariant list you can't provide an implementor that has a derived generic type argument

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