001
001

Reputation: 13533

Generics of unknown type

I'm creating a settings class like so:

class Setting<T> {...}

I am storing all the settings in a tree like so:

class SettingsNode {
    public Setting<> setting;
    public Setting<> child;
    public Setting<> sibling;
}

This obviously doesn't compile because of the <>. I can have multiple types (Setting<int>, Setting<string>, etc) so I don't know how to create the SettingsNode.

So I changed it to:

class SettingsNode {
    public object setting;
    public object child;
    public object sibling;
}

But am having trouble with casting to the right type:

// Create a setting from a type name as text
SettingsNode tmp = new SettingsNode();

Type genericType = typeof(Setting<>);
Type[] typeArgs = { Type.GetType("System.Int32") };
Type cType = genericType.MakeGenericType(typeArgs);
tmp.setting = Activator.CreateInstance(cType);

// Here's where I have a problem
Type baseType = typeArgs[0];
((Setting<baseType>)(tmp.setting)).SomeFunction();

The error on that last line is:

The type or namespace name 'baseType' could not be found (are you missing a using directive or an assembly reference?)

How can I do this? Thanks!

Upvotes: 2

Views: 4104

Answers (7)

Xavier Guzman
Xavier Guzman

Reputation: 460

As stated by , I do not think generics should be used for this scenario. If you want / need to stick to it, you could try using a class with multiple generics.

public class SettingsNode<TSetting, TChild, TSibbling>
{
    public Setting<TSetting> Setting { get; set; }
    public Setting<TChild> Child { get; set; }
    public Setting<TSibbling> Sibling { get; set; }
}

public class Setting<T>
{
    public T Value;
}

SettingsNode<String, int, int> node = new SettingsNode<string, int, int>();
node.Sibling = new Setting<int>();
node.Setting = new Setting<string>();
node.Child = new Setting<int>();

Here's a different approach I took once when I did not know what type to expect. This involves the usage of dynamic keyword, so requires c# 4.0+

Upvotes: 0

Daniel A.A. Pelsmaeker
Daniel A.A. Pelsmaeker

Reputation: 50326

If Setting<T> defines a method:

public T Transform(T value);

...and you could define a field of type Setting<?>, then what type of parameter would Transform accept? object is the only type common to all possible T. So you lose all type safety. And what type of parameter would Transform return? Again, object, so you have to cast everything.

You should view each generic class instance as a different type: manually copying your Setting<T> class as SettingOfInt32 and replacing all T by Int32, is simply automated by the generic syntax Setting<Int32>. So, instead of thinking Setting<Int32> and Setting<String> are both Setting<T>, you should think that you have SettingOfInt32 and SettingOfString that are both derived from Object.

Imagine you want to be able to put objects of type SettingOfInt32 and SettingOfString in a field, what type must the field have? Of course, a type that is common to both, such as object. Now you might realize that if you want to call any method that exists in both SettingOfInt32 and SettingOfString, you should move that method to a common base class, e.g. Setting. Then your field's type must be Setting and all is well.

public class Setting<T> : Setting { }

public abstract class Setting { }

This also forces you to deal with things like the Transform example above. Do you include it in the common base class? Maybe, maybe not. What types do you use instead of T? Maybe object, maybe something else. Do you do explicit type checking? Possibly...

Upvotes: 0

cuongle
cuongle

Reputation: 75306

One option is you can go ahead with dynamic keyword:

class SettingsNode
{
    public dynamic setting;
    public dynamic child;
    public dynamic sibling;
}

So you can do like this:

SettingsNode tmp = new SettingsNode();
tmp.setting = new Setting<int>();

tmp.setting.SomeFunction()

Without need using reflection

Upvotes: 3

Boris B.
Boris B.

Reputation: 5024

If you really want to go down that route make an abstract non-generic base class abstract class Setting and a generic Setting<T> : Setting. Define all common methods in non-generic one using object instead of a T and use the generic one just as an adapter which calls super's methods/properties and does casting object<=>T in each method/property.

Upvotes: 1

SLaks
SLaks

Reputation: 887453

Generics cannot do that.
The whole point of generics is to specify the type parameter within the type system – at compile time.
You're trying to make those fields have no known type.

Instead, you should make the generic class inherit from a non-generic SettingsBase base class (or interface), and make the fields of that type.
Calling code can cast them back to the actual generic types.

Upvotes: 0

Heinzi
Heinzi

Reputation: 172260

I don't think generics are the right tool for this. Generics are a compile-time tool: If the generic type is not known at compile time, there is no advantage over a non-generic type.

So, unless you plan to explicitly use Setting<Int32> and Setting<String> in your code, there is just no point in using generics here, and you might as well stick with Setting.

Upvotes: 0

Kamil Krasinski
Kamil Krasinski

Reputation: 529

Why not have:

class SettingsNode<T> {
    public Setting<T> setting;
    public Setting<T> child;
    public Setting<T> sibling;
}

Upvotes: 1

Related Questions