0

I am trying to remove duplicate code for collections of System.Func with variable numbers of arguments. (This is used to allow code to "hook" into the logic of other components.) So I am trying to remove several almost identical methods and use generic methods instead.

To make it work I wrapped them in a HookContainer class and made it implement a common interface.

public interface IHookContainer
{
    void Add<TFunc> (string filterName, int priority, KeyValuePair<string, TFunc> action);
}

public class HookContainer<T>:IHookContainer
{
    Dictionary<string,OrderedDictionary<string,T>> dict = new Dictionary<string, OrderedDictionary<string, T>> ();

    public void Add<T> (string filterName, int priority, KeyValuePair<string, T> action)
    {
         // Add an item to the Dictionary
    }
}

This allows me to put them all in a single Dictionary which I can access with the Type

Dictionary<Type,IHookContainer> containers = new Dictionary<Type, IHookContainer> (){
{
    typeof(HookContainer<System.Func<object,object>>),new HookContainer<System.Func<object,object>>()
},
// More dictionary entries like this (with more parameters)
};


    public string AddFilter<T> (string filterName, T action, string filterTag = null, int priority=0)
    {

            KeyValuePair<string, T> data = new KeyValuePair<string, T> (filterTag, action);
            if (containers.ContainsKey (typeof(T))) {
                    IHookContainer container = containers [typeof(T)];
                    container.Add<T> (filterName, dictPriority, data);


            }

            return filterTag;

    }

Now I get a compile error saying:

cannot convert `System.Collections.Generic.KeyValuePair<string,T>' 
expression to type `System.Collections.Generic.KeyValuePair<string,T>'

So it's apparently lacking some information that I take for granted. Or I am thinking wrong. Or both. Why can't it convert a type to ...itself??

I apparently cannot use the same Type parameter from the Generic class for the generic method if the implemented interface. Sadly, the type cannot be inferred either. Is there any way to make this work?

There is also a warning that the type paramater name is the same as the outer type paramemter. Can I tell the compiler that this is completely intended?

Thanks for any help. I am using C# 4.0

Update:

I can get the code to work by declaring the HookContainer with a second Type parameter and using this for passing it into the Add method.

So it looks like this now:

public class HookContainer<T,U>:IHookContainer
{
    Dictionary<string,OrderedDictionary<string,T>> dict = new Dictionary<string, OrderedDictionary<string, T>> ();
    public void Add<U> (string filterName, int priority, KeyValuePair<string, T> action)
    {
        // Add an item to the Dictionary
    }

}

This, of course requires me to redundantly pass the Type of the Container twice when declaring the Dictionary:

Dictionary<Type,IHookContainer> containers = new Dictionary<Type, IHookContainer> (){
{
typeof(HookContainer<System.Func<object,object>,System.Func<object,object>>),new HookContainer<System.Func<object,object>,System.Func<object,object>>()
}
};

So while this might work, it looks and feels ridiculous. Is there any way to just (re)use a single Type parameter?

2
  • I'm getting "The non-generic type 'System.Collections.Specialized.OrderedDictionary' cannot be used with type arguments." Commented Jul 11, 2014 at 18:26
  • I'm using this implementation of OrderedDictionary github.com/johndkane/GenericOrderedDictionary/blob/master/… Commented Jul 11, 2014 at 18:41

2 Answers 2

2

Update

I misread your question. The way you implement this, is not what you want I think. The declaration in your interface is different from what you want in your implementation. In your interface declaration you specify that implementations of this interface should have a generic function (which can be called with all kinds of types). In your implementation you want to limit this just to the type that you specify in the class. The class implementation will be for one specific type.

I.e. the IHookContainer interface specifies you can call the Add function with int,string, etc. Whereas in HookContainer<string> you can only call Add with a string argument.

A more suitable implementation to what you want, but you can not store that in the Dictionary is:

public interface IHookContainer<T>
{
   Add(string filterName, int priority, KeyValuePair<string, T> action);
}

What a solution can be (what I might do) is to change the Dictionary to a List and create a function that retrieves the correct IHookContainer<T>. Or keep your dictionary when the number of items is large and address the correct one by key.

private List<object> containers = new List<object>();
private IHookContainer<T> GetCorrectHookContainer<T>()
{
    // might need to add when not available?
    return containers.OfType<IHookContainer<T>>().FirstOrDefault();
}

your AddFilter function will be something like this (pseudo-code)

public void AddFilter<T>(...)
{
    GetCorrectHookContainer<T>().Add(...);
}

Old:

Because you specified the type argument (again) in your function, the T of the class is different from the T in the function. The T argument in your generic function 'hides' the T of the class. You can just use it. You do not have to specify this in your function. Your implementation can be:

public void Add(string filterName, int priority, KeyValuePair<string, T> action)
{
     // Add an item to the Dictionary
}

example use:

var x = new HookContainer<int>();
x.Add("test", 1, new KeyValuePair<string,int>("test", 4)); 
Sign up to request clarification or add additional context in comments.

2 Comments

Okay, what you say makes sense. So it's not possible at all unless I drag 2 identical (but technically different) Type parameters all the way through every method that accesses them?
The problem with making the interface itself generic (instead of just the method) is that I cannot put them all in the same Dictionary anymore, defeating the point of the whole setup. Or am I mistaken?
1

It seems that the problem is TFrom from Add and T from HookContainer are two different generic types.

I don't know if it's possible, but I would use the C's main signature (int argc, char[] argv) instead of the Func functions:

System.Func<int, object[], object>

2 Comments

This does not work, I'm afraid. I cannot add anything to the Dictionary. As soon as I cast it down to IHookContainer<object>, it returns null when I try to get it out again. Thank you very much for your time, though
Updated the answer with the example I'm using to store hooks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.