B Reed
B Reed

Reputation: 23

C# Generic Class field possible

I am new to C# (Java developer), I want to have a class field that is a generic list, actually it is a dictionary of lists:

protected IDictionary<String, IList<Object>> filters;

I have code that sets

public void SetFilters(String key, params Object[] values) {
    if (key == null || values == null) {
        throw new ArgumentNullException("Must have filter name and values.");
    }
    if (filters == null) filters = new Dictionary<String, IList<Object>>();
    IList<Object> fvalues = values.ToList();
    filters.Add(key, fvalues);         
}

But when my code tries to retrieve and cast the IList<Object> back to IList<String> or IList<int> I get an InvalidCastException.

I thought I would make the list generic:

protected IDictionary<String, IList<T>> filters; //does not complile

protected IDictionary<String, IList<T>> filters  where T: Object;//does not compile either

I cannot make the class generic since the dictionary will have lists of Strings or int. In Java, Integer and Strings are all Objects, so this was not an issue with IList<? extends Object>.

Thanks!

Upvotes: 1

Views: 243

Answers (3)

Grax32
Grax32

Reputation: 4059

In order to avoid the error casting, you need to create a typed list in the first place. To do that you should use a generic.

For the dictionary item type, you can use IList or object but you will need to store a strongly typed list as the item value.

If you make SetFilters a generic method, then it can make a properly typed list to store in the dictionary. I included a GetFilters method that returns the list that matches the key. i.e. values.ToList() will create a List<T>

public class FilterManager
{
    protected IDictionary<String, IList> filters = new Dictionary<string, IList>();

    public void SetFilters<T>(String key, params T[] values)
    {
        if (key == null || values == null)
        {
            throw new ArgumentNullException("Must have filter name and values.");
        }

        IList fvalues = values.ToList();
        filters.Add(key, fvalues);
    }

    public IList<T> GetFilters<T>(string key)
    {
        return (IList<T>)filters[key];
    }
}

Call it like this

var filterManager = new FilterManager();
filterManager.SetFilters("MyIntegerFilters", 3, 4, 5);
filterManager.SetFilters("MyStringFilters", "A", "B", "C");

var intFilters = filterManager.GetFilters<int>("MyIntegerFilters");
var stringFilters = filterManager.GetFilters<string>("MyStringFilters");

You will get an exception if you call

var filters = filterManager.GetFilters<int>("MyStringFilters");

because it will try to convert a List<string> to a List<int>

Upvotes: 0

Nope
Nope

Reputation: 22339

You could use System.Collection.IList, similar to this:

public class Foo
{
    public IDictionary<String, IList> filters;
    public void SetFilters(String key, params object[] values)
    {
        if (key == null || values == null)
        {
            throw new ArgumentNullException("Must have filter name and values.");
        }

        if (filters == null)
        {
            filters = new Dictionary<String, IList>();
        }

        IList fvalues = values.ToList();
        filters.Add(key, fvalues);
    }
}

You could then use it like this:

var foo = new Foo();

foo.SetFilters("Key1", 1,2,3);
foo.SetFilters("Key2", "a","b","c");
foo.SetFilters("Key3", new {a = 1, b = 2}, new {c = 1, d = 2});

You still have then the issue of casting back each list type into the expected type when accessing and using it.


DEMO - Using IList


Upvotes: 2

Igor
Igor

Reputation: 281

Does it help? I created a new generic class.

internal class Program
{
    private class Reed<T>
    {
        private IDictionary<String, IList<T>> filters;
        public void SetFilters(String key, params T[] values)
        {
            if (key == null || values == null)
            {
                throw new ArgumentNullException("Must have filter name and values.");
            }

            if (filters == null)
                filters = new Dictionary<String, IList<T>>();
            IList<T> fvalues = values.ToList();
            filters.Add(key, fvalues);
        }
    }

    private static void Main(string[] args)
    {
        var r1 = new Reed<string>();
        r1.SetFilters("test", "one", "two", "three");
        var r2 = new Reed<int>();
        r2.SetFilters("test", 1, 2, 3);
    }
}

Upvotes: 0

Related Questions