Generics in C#, Using Type of a Variable as Parameter

Generics in C#, using type of a variable as parameter

The point about generics is to give compile-time type safety - which means that types need to be known at compile-time.

You can call generic methods with types only known at execution time, but you have to use reflection:

// For non-public methods, you'll need to specify binding flags too
MethodInfo method = GetType().GetMethod("DoesEntityExist")
.MakeGenericMethod(new Type[] { t });
method.Invoke(this, new object[] { entityGuid, transaction });

Ick.

Can you make your calling method generic instead, and pass in your type parameter as the type argument, pushing the decision one level higher up the stack?

If you could give us more information about what you're doing, that would help. Sometimes you may need to use reflection as above, but if you pick the right point to do it, you can make sure you only need to do it once, and let everything below that point use the type parameter in a normal way.

C#, instantiating a generic type - with variable type argument?

You just can't use C# generics the way you're trying to do in your snippet.

In order to use [C#] generics, the actual object type must be known at compile time.

You're trying to dynamically pass the object type as a type parameter. This is simply not possible.

Edit

Yes, it is possible to dynamically create generic objects using reflection. After all, generics is implemented both as a compile-time C# construct and as a .NET framework feature (as opposed to, say, Java, where it is only a compile-time feature based on Type Erasure). So, in .NET, through reflection, it is possible to implement the latter "bypassing" the former (which, again, would be impossible in Java).

But the OP clearly does not need that.

After all, entries is a List<IEntry>. IOW, the entries container does not "know" the concrete type of its elements (since it is bound to an interface). So, if each element to be add already implements IEntry, then this would be enough:

void Foo(params IEntry[] objects)
{
var entries = new List<IEntry>();
foreach(var o in objects)
{
entries.Add(o);
}

...
}

OTOH, if those objects do not implement IEntry, then the OP just need a pure, ordinary, old-school list of untyped objects:

void Foo(params object[] objects)
{
var entries = new List<object>();
foreach(var o in objects)
{
entries.Add(o);
}

...
}

So using reflection in order to dynamically create a generic container, even if possible, seems to be overkill for this particular use case.

Initializing a Generic variable from a C# Type Variable

What you mean by this part is possible:

new AnimalContext<a.GetType()>();

Obviously that exact syntax is wrong, and we'll get to that, but it is possible to construct an instance of a generic type at runtime when you don't know the type parameters until runtime.

What you mean by this part is not:

AnimalContext<a.GetType()> a_Context

That is, it is impossible to type a variable as a generic type if you don't know the type parameters at compile-time. Generics are compile-time constructs, and rely on having the type information available at compile-time. Given this, you lose all the benefits of generics if you don't know the types at compile-time.

Now, to construct an instance of a generic type at runtime when you don't know the type until runtime, you can say:

var type = typeof(AnimalContext<>).MakeGenericType(a.GetType());
var a_Context = Activator.CreateInstance(type);

Note that the compile-time type of a_context is object. You will have to cast a_context to a type or interface that defines the methods you need to access. Often what you'll see people do here is have the generic type AnimalContext<T> implement some interface (say IAnimalContext) or inherit from a non-generic base class (say AnimalContext) that defines the methods they need (so then you can cast a_context to the interface or the non-generic base class). Another alternative is to use dynamic. But again, keep in mind, you have none of the benefits of generic types in doing this.

Pass An Instantiated System.Type as a Type Parameter for a Generic Class

You can't do this without reflection. However, you can do it with reflection. Here's a complete example:

using System;
using System.Reflection;

public class Generic<T>
{
public Generic()
{
Console.WriteLine("T={0}", typeof(T));
}
}

class Test
{
static void Main()
{
string typeName = "System.String";
Type typeArgument = Type.GetType(typeName);

Type genericClass = typeof(Generic<>);
// MakeGenericType is badly named
Type constructedClass = genericClass.MakeGenericType(typeArgument);

object created = Activator.CreateInstance(constructedClass);
}
}

Note: if your generic class accepts multiple types, you must include the commas when you omit the type names, for example:

Type genericClass = typeof(IReadOnlyDictionary<,>);
Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2);

How to pass variable of type Type to generic parameter

You need to use reflection for this.

var method =
typeof(MetaDataUtil)
.GetMethod("GetColumnasGrid")
.MakeGenericMethod(new [] { type })
.Invoke(null, null);

Generic Type as a Variable

Essentially you're trying to go from reflection to generics and back to reflection.

Since the method in question simply steps back into reflection a better method is to create an overload taking a type, like this:

public void CreateTables<T>()
{
CreateTables(typeof(T));
}

public void CreateTables(Type tableType)
{
string name = tableType.Name;

var fields = tableType.GetProperties().Select(t => new
{
key = t.Name.ToLower(CultureInfo.InvariantCulture),
value = SqLiteUtility.GetSQLiteTypeString(t.PropertyType)
})
.ToDictionary(
t => t.key,
t => t.value);

CreateTable(name, fields);
}

This way you can still call it with a specific type, generics-wise, or you can call it from your loop by simply passing the type object.


Having said all that, if you cannot change the method to take a Type object, here's how you need to invoke it using reflection:

var m = GetType().GetMethod("CreateTables").MakeGenericMethod(new Type[] { type });
m.Invoke(this, null);

Note! This assumes you only have one such method available, that it is public, etc.

Passing a variable number of generic type parameters

If, as you say, you want to be able to pass completely unrelated lists, maybe you should accept the base IList interface:

public void Export(string FileName, params IList[] Data)

All List<T> objects implement IList, as well as the generic IList<T>, so you can enforce that.

Actually, depending on what you need to do with these lists in the Exporter, perhaps enforcing IEnumerable is enough for you, if all you need is forward-only read-once functionality.

Best way to decide is to determine what the shared functionality you need for all parameters, and define your method signature to enforce that functionality.

UPDATE

Based on your comment, it seems what you want is possible in C# 4.0 using covariant/contravariant generics, meaning that a generic ISomething<out object> could receive an ISomething<string>. The problem is that IList<T> doesn't define a covariant generic parameter, so passing IList<string> for IList<object> doesn't work.

However, IList<T> inherits IEnumerable<out T>, which DOES define a covariant parameter. So if your Export method receives a params IEnumerable<object>[] data parameter, you SHOULD be able to pass it any IList<T> you want.

Using Type objects as Type Parameters for Generics in C#

You can dynamically create an instance of the type :

public void test()
{
Type someType = getSomeType(); // get some System.Type object
Type openType = typeof(MyGeneric<>);
Type actualType = openType.MakeGenericType(new Type[] { someType });
object obj = Activator.CreateInstance(actualType);
}

However you can't declare a variable of this type, since you don't know the actual type statically.



Related Topics



Leave a reply



Submit