Parameterize a generic interface - Create a Dictionary<Type, Interface<T>>?

走远了吗. 提交于 2019-12-12 13:54:15

问题


Sorry the title is confusing, I don't know if I said it right, not sure what this thing is called... Feel free to edit it after you read the question if you want.

I'm refactoring my old code when I noticed there are a lot of places where I could use the Strategy pattern. I have an inventory system, and items - There are more than one ways to add an item - Normal way, forceful way, etc. You could also swap items, again, with more than one way - So I figured those are good places to use that pattern.

Here is what I have, for adding items:

public interface IAddingStrategy<T> where T : AddArgs
{
    bool Add(T args);
}

public class NormalAdd : IAddingStrategy<NormalAddArgs>
{
    public bool Add(NormalAddArgs args)
    {
        // normal adding logic...
    }
}

public class ForceAdd : IAddingStrategy<ForceAddArgs>
{
    public bool Add(ForceAddArgs args)
    {
        // force adding logic...
    }
}

// other strategies...

public abstract class AddArgs
{
}

public class NormalAddArgs : AddArgs
{
    public readonly param1 p1;
    public readonly param2 p2;
    etc;

    public NormalAddArgs(param1 p1, param2 p2, etc)
    {
        this.p1 = p1;
        this.p2 = p2;
        this.etc = etc;
    }
}

public class ForceAddArgs : AddArgs
{
    public param3 p3;

    public ForceAddArgs(param3 p3)
    {
        this.p3 = p3;
    }
}

// other adding args...

Now what I'm trying to do - But don't know how to, is:

public class Adder
{
    private Dictionary<Type, SOMETHING> dic = new Dictionary<Type, SOMETHING>();
    public Adder()
    {
        dic.Add(typeof(NormalAdd), new NormalAdd());
        dic.Add(typeof(ForceAdd), new ForceAdd());
        dic.Add(typeof(EtcAdd), new EtcAdd());
    }
}

What should SOMETHING be? I want it to be any adding strategy - But if I go for IAddingStrategy - it doesn't work since it requires me to specify the <T> parameter which if I do, the dictionary can't hold all the possible types of strategies... ;(

Any ideas how to go about this? what should SOMETHING be? Am I right of using the strategy pattern in this situation? If not, what's the best approach to this?

Thanks all for your help.

EDIT: One way around this, would be something like this:

public interface IAddingStrategy
{
    void Add(AddArgs args);
}

public class NormalAdd: IAddingStrategy
{
    public void Add(AddArgs args)
    {
        if (args is NormalAddArgs)
            // normal add logic
    }
}

public class ForceAdd: IAddingStrategy
{
    public void Add(AddArgs args)
    {
        if (args is ForceAddArgs)
            // force add logic
    }
}

Then the dictionary would simply be <Type, IAddingStrategy>

But really, I don't like the if thing, it just doesn't feel right - what if someone passes the wrong type of arg? that is something only detected at run-time, I want to detect the thing at compile-time. - Using generics I was able to achieve that.


回答1:


Per my comments: it may make sense to minimize the exposure of the generic interface, rather than requiring generic arguments to the chain of callers. One way to do this is to implement both a generic and non-generic interface.

PROS:

  • You can now work with a non-generic interface.
  • Reasonable type safety (in most cases).

CONS:

  • Casts are required.
  • Every method needs two implementations.
  • Type safety isn't guaranteed by the compiler.
/// <summary>Some common base type.</summary>
public interface IAddArgs {}

/// <summary>Non-generic interface.</summary>
public interface IAddingStrategy
{
    void Add( IAddArgs obj );
}

/// <summary>Generic version.</summary>
/// <typeparam name="T"></typeparam>
public interface IAddingStrategy<T> : IAddingStrategy
    where T : IAddArgs
{
    void Add( T obj );
}

public class NormalAddArgs : IAddArgs {}

public class NormalAdd : IAddingStrategy<NormalAddArgs>
{
    public void Add( NormalAddArgs obj )
    {

    }

    public void Add( IAddArgs obj )
    {
        Add( (NormalAddArgs)obj );
    }
}

This does leave the nagging problem of how to create the correct type of arguments in the first place, i.e. what class has enough information to create an instance of NormalAddArgs so that it can be passed to the appropriate strategy?

One way is to ask each type to create these types for you:

/// <summary>Non-generic interface.</summary>
public interface IAddingStrategy
{
    void Add( IAddArgs obj );

    // "context" can be a type that provides additional info
    IAddArgs CreateAddArgs( object context );
}

public class NormalAdd : IAddingStrategy<NormalAddArgs>
{
    public void Add( NormalAddArgs obj ) { }

    public void Add( IAddArgs obj )
    {
        Add( (NormalAddArgs)obj );
    }

    public IAddArgs CreateAddArgs( object context )
    {
        return new NormalAddArgs();
    }
}



回答2:


I don't think you are going to be able to have a Dictionary<Type, Interface<T>> in your case since you would need to define T to be common to all values in the dictionary.

Jon Skeet gives an excellent answer to a similar question: Generic Dictionary with a value as a Interface with a Generic reference. I hope it helps you too.



来源:https://stackoverflow.com/questions/20084917/parameterize-a-generic-interface-create-a-dictionarytype-interfacet

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!