dijxtra
dijxtra

Reputation: 2751

Finding out type of a generic class

Lets say I have a generic class:

class Foo {
//    protected Type t;

//    public void SetT(string strval) {
//        ((Foo<t>)this).Set(strval);
//    }
}

class Foo<T> : Foo {
    private T val;

    public void Set(string strval) {
        if (this is Foo<float>) {
            this.val = float.Parse(strval);
        } else if (this is Foo<int>) {
            this.val = int.Parse(strval);
        }
    }
}

Now I create an object and put it in an ArrayList:

ArrayList a = new ArrayList();
a.Append(new Foo<float>);

And then I forget the type of Foo<>. Now, how do I Set? I tried the obvious candidates:

(Foo)a[0].Set("5.0");
(Foo<a[0].GetType()>)a[0].Set("5.0");

but those failed.

Is there a way I can call that Set method without explicitly knowing the type of Foo<>?

If not, can I somehow save type of Foo into Foo.t, and then uncomment and use Foo.SetT?

Ah, generics. Very nice tool if you know how to use them :-)

Regards, dijxtra

Upvotes: 0

Views: 162

Answers (6)

BLUEPIXY
BLUEPIXY

Reputation: 40145

using System;
using System.Collections;
using System.Collections.Generic;

class Foo {
}

class Foo<T> : Foo {
    private T val;

    public void Set(string strval) {
        var _type = typeof(T);
        val = (T)(_type.InvokeMember("Parse", System.Reflection.BindingFlags.InvokeMethod, null, null, new Object[] { strval }));
    }
    override public string ToString(){
        return String.Format("{0}", val);
    } 
}
class Sample {
    static void Main(string[] args){
        ArrayList a = new ArrayList();
        a.Add(new Foo<float>());
        a.Add(new Foo<int>());

        dynamic ax = a[0];
        ax.Set("5.5");
        ax = a[1];
        ax.Set("55");
//EDIT
//But I may have to set the float value to Foo <int> If you forgot
//        ((Foo<float>)a[0]).Set("5.5");
//        ((Foo<int>)a[1]).Set("55");
        Console.WriteLine("{0},{1}", a[0], a[1]);
    }
}

Upvotes: 0

Chaitanya
Chaitanya

Reputation: 5293

If you want to know the type parameter that was used in you generic, use the GetGenericArguments method.

class Foo<T> {
    int input_as_int;
    float input_as_float;


    public void Set(string strval) {
        if (this.GetType().GetGenericArguments().First() == typeof(float)) {
            this.input_as_float = float.Parse(strval);
        } else if (this.GetType().GetGenericArguments().First() == typeof(int)) {
            this.input_as_int = int.Parse(strval);
        }
        // Else .. throw an exception? return default value? return 0? what makes sense to your application
    }

or alternately if you could by pass the Interface entirely and pass the input string in the constructor.

public class Foo<T>
{
    public Foo (string input)
    {
        var typeConverter = TypeDescriptor.GetConverter(typeof(T));

        if (typeConverter.CanConvertFrom(typeof(string)))
        {
            Value = (T)typeConverter.ConvertFromString(input);
        }
        else
        {
            throw new InvalidOperationException();
        }
    }

    public T Value { get; set;
    }
}

then you can just use it like so.

var test = new List<int> Foo ("3");

Upvotes: 0

dlev
dlev

Reputation: 48596

There's absolutely no reason to be using generics here. Generics are intended to be used when the type of operations you will be performing are generic. In other words, they are independent of the type(s) on which they are performed. You are doing the opposite: the operation will be different depending on the types.

Given that, you should remove the generic parameter, make Set() and Foo abstract, and derive appropriate classes to handle the different types:

abstract class Foo
{
    public abstract void Set(string value);
}

class FooDouble : Foo
{
    double val;
    public override void Set(string value)
    {
        this.val = double.Parse(value);
    }
}

// Etc.

Then, you should be storing your Foos in a List<T>:

List<Foo> fooList = new List<Foo>();
fooList.Add(new FooDouble());

Later, you can say this:

fooList[0].Set("5.0");

And it will just work! No need to remember!

Upvotes: 3

Adam Lear
Adam Lear

Reputation: 38758

In addition to what Jimmy pointed out for your base class, you could use a generic collection instead of an ArrayList and make use of a type converter:

public interface IFoo
{
    void Set(string value);
}

public class Foo<T> : IFoo
{
    private T val;

    public void Set(string value)
    {
        var typeConverter = TypeDescriptor.GetConverter(typeof(T));

        if(typeConverter.CanConvertFrom(typeof(string)))
        {
            val = (T)typeConverter.ConvertFromString(value);
        }
        else 
        {
            throw new InvalidOperationException();
        }
    }
}

The above will work with either your ArrayList:

ArrayList a = new ArrayList();
a.Append(new Foo<float>());

((IFoo)a[0]).Set("123.4");

Or with a typed collection:

List<IFoo> list = new List<IFoo>();
list.Add(new Foo<float>());

list[0].Set("123.4");

As an added bonus, you don't need to have an if statement in your Set method and try to account for all possible types.

Upvotes: 1

Jimmy
Jimmy

Reputation: 91432

You want to override the implementation of Set in the derived classes.

class Foo { 
    public virtual void Set(string val);
}
class Foo<T> : Foo { 
    public override void Set(string val);
}

Upvotes: 3

David Brown
David Brown

Reputation: 36239

One way is to make your generic Foo class implement an interface:

interface IFoo {
    void Set(string strval);
}

class Foo<T> : IFoo {
    private T val;

    public void Set(string strval) {
        ...
    }
}

Then you can cast to IFoo and call Set():

((IFoo)a[0]).Set("5.0");

Upvotes: 4

Related Questions