lbrahim
lbrahim

Reputation: 3810

Invoke a method with generic argument

I have a generic class and I take its type as a parameter type e.g. int for a method in my class like below:

public class UnlimitedGenericArray<T>
{
 private void InsertItem(T item)
 {
  this.array[0] = item;
 }
}

Now, when I want to invoke the InsertItem()from a console application for example how can I know the argument's type at runtime?

    static void Main(string[] args)
    {
        UnlimitedGenericArray<int> oArray = new UnlimitedGenericArray<int>();
        while(true)
        {
         var userInput = Console.Readline();
         oArray.InsertItem(userInput);
        }
    }

I can write InsertItem(object item) instead and then cast in the method like below:

private void InsertItem(object item)
     {
      this.array[0] = (T)Convert.ChangeType(item, typeof(T));
     }

But this is probably not a good practice. I also need to know the type of argument at client so that I can parse there and then invoke the method. I am new to Generics so help me out here please.

Upvotes: 0

Views: 284

Answers (3)

asawyer
asawyer

Reputation: 17808

The only option that comes to mind is to hand the class a method of converting from a known type into in the form of a Type/Func dictionary, like this:

public class UnlimitedGenericArray<T>
{
    public IList<T> List { get; set; }

    private IDictionary<Type,Func<object,T>> InserterFuncDict{get;set;}

    public UnlimitedGenericArray(IDictionary<Type,Func<object,T>> inserterDict)
    {
        this.List = new List<T>();

        this.InserterFuncDict = inserterDict;
    }

    public void AddItem(object item)
    {
        var itemType = item.GetType();
        if(itemType == typeof(T))
        {
            this.List.Add((T)item);
        }
        else if(this.InserterFuncDict.ContainsKey(itemType))
        {
            this.List.Add(this.InserterFuncDict[itemType](item));
        }
        else 
        {
            var msg = "I don't know how to convert the value: {0} of type {1} into type {2}!";
            var formatted = string.Format(msg,item,itemType,typeof(T));
            throw new NotSupportedException(formatted);
        }
    }

}

Then the usage would look like this:

var arr = new UnlimitedGenericArray<int>(new Dictionary<Type,Func<object,int>>()
{
    { typeof(string), v => int.Parse(v.ToString()) }
});

// ok! int == T
arr.AddItem(123); 
// ok, a mapping is provided
arr.AddItem("123"); 
// Error! 
//"I don't know how to convert the value: False of type System.Boolean into type System.Int32!"
arr.AddItem(false);

Then if say, you wanted to add boolean support you could change the declaration to:

var arr = new UnlimitedGenericArray<int>(new Dictionary<Type,Func<object,int>>()
{
    { typeof(string), v => int.Parse(v.ToString()) }
    { typeof(bool), v => bool.Parse(v.ToString()) }
});

Just keep adding to the type convert dictionary as needed.

Upvotes: 0

DasKr&#252;melmonster
DasKr&#252;melmonster

Reputation: 6060

When you specify the generic parameter as int, you may as well assume that type later on. So your code in the Console application becomes:

static void Main(string[] args)
{
    // Specifying int here ...
    UnlimitedGenericArray<int> oArray = new UnlimitedGenericArray<int>(); 
    while(true)
    {
     string userInput = Console.ReadLine();
     int number = int.Parse(userInput);
     // ... therefore we know that the method below requires an int
     oArray.InsertItem(number);
    }
}

Upvotes: 0

Eric Lippert
Eric Lippert

Reputation: 659956

You don't know the type in the body of the method. If you knew the type then you would not have used generics in the first place.

You might not want to use generics here. If you need to make a decision based on the type then your method is not generic.

Upvotes: 10

Related Questions