views:

160

answers:

3

I am trying to do the following but I think I must be missing something...(fairly new to generics)

(Need to target .NET 2.0 BTW)

interface IHasKey
{
    string LookupKey { get; set; }
}
...

public static Dictionary<string, T> ConvertToDictionary(IList<T> myList) where T : IHasKey
{
    Dictionary<string, T> dict = new Dictionary<string, T>();
    foreach(T item in myList)
    {
        dict.Add(item.LookupKey, item);
    }

    return dict;
}

Unfortunately, this gives a "Constraints are not allowed on non-generic declarations" error. Any ideas?

+5  A: 

You have not declared the generic parameter.
Change your declaration to:

public static Dictionary<string, T> ConvertToDictionary<T> (IList<T> myList) where T : IHasKey{
}
Paolo Tedesco
What's the point of using "T where IHasKey" instead of declaring it Dictionary<string,IHasKey> in this scenario?
Mikael Svenson
@Mikael: in this way the return value is a Dictionary<string, Something> instead of a Dictionary<string, IHasKey>, so when you access the value you avoid an extra cast and are sure which objects your dictionary contains.
Paolo Tedesco
@orsogufo: Good point! But if you were to think along the lines that the input list might contain different implementations of IHasKey, then boxing is unavoidable.
Mikael Svenson
@Mikael: ok, but note that you could still use the same method with T=IHasKey. In any case I see no disadvantage in the generic approach.
Paolo Tedesco
@orsogufo: I agree :) So it moves over to a philosophical question: Should you define it generically, when you know you will instantiate it with an interface and not a concrete type?
Mikael Svenson
+1  A: 

Try something like this

public class MyObject : IHasKey
{
    public string LookupKey { get; set; }
}

public interface IHasKey
{
    string LookupKey { get; set; }
} 


public static Dictionary<string, T> ConvertToDictionary<T>(IList<T> myList) where T: IHasKey 
{ 
    Dictionary<string, T> dict = new Dictionary<string, T>(); 
    foreach(T item in myList) 
    { 
        dict.Add(item.LookupKey, item); 
    } 
    return dict; 
}

List<MyObject> list = new List<MyObject>();
MyObject o = new MyObject();
o.LookupKey = "TADA";
list.Add(o);
Dictionary<string, MyObject> dict = ConvertToDictionary(list);

You forgot the Generic Paramter in the method

public static Dictionary<string, T> ConvertToDictionary<T>(IList<T> myList) where T: IHasKey
astander
A: 

Since the classes in the input list are different (as you say in your comment) you can either implement it like suggested by @orsogufo, or you could just as well implement your signature on the interface itself:

public static Dictionary<string, IHasKey> ConvertToDictionary(IList<IHasKey> myList) 
{
    var dict = new Dictionary<string, IHasKey>();
    foreach (IHasKey item in myList)
    {
        dict.Add(item.LookUpKey, item);
    }
    return dict;
}

Using the generic declaration is best if you have a list of one specific implementation of the interface as noted in the comments to the other answer.

Mikael Svenson
Yes, but as orsogufo pointed out, this requires casting of IHasKey objects to access other members after retrieval from the dictionary.
Damien
But if the input list is of type IList<IHasKey> which I assume it is since you have different classes in it, it makes no difference which implementation you use, since you would get an extra boxing for both if you wanted to do something with a specific class.
Mikael Svenson
The input list isn't necessarily IList<IHasKey> but if it were you'd be right
Damien
@Mikael, the input list could be a IList<Customer> where Customer : IHasKey. In that case, we get back a Dictionary<string, Customer> instead of a Dictionary<string, IHasKey>. The Customer valued Dictionary is more useful (avoids needless casting).
David B
@David B: In scenarios where the List has one class type I agree 100%, but @Damien said in a comment that the list could contain several implementations of IHasKey (the way I understood it), and in that case it has to be a IList<IHasKey>. Just wanted to get the actual scenario @Damien wanted to cover.
Mikael Svenson
Then it is a miscommunication. There are several classes that implement IHasKey, but only one class used for each call.
David B