Katriel
Katriel

Reputation: 123632

Dynamic type passing and instantiation -- how?

EDIT: changed Activator, still doesn't work.

So I'm pretty (very) new to C# and I'm pretty sure this is a dupe, but I've looked through the previous questions and I still can't work out all the points.

I am trying to reduce code smell by replacing some repeated code with a map over a generic list. Specifically, I have code that looks like

var fooNode = node as IFoo;
var barNode = node as IBar;
var bazNode = node as IBaz;
...

if(fooNode != null)
    return new FooThing();
if(barNode != null)
    return new BarThing();
if(bazNode != null)
    return new BazThing();
...

and I want to generalise it.


Here's my attempt:

var types = new Dictionary<Type, Type>
{
    {typeof(IFoo), typeof(FooThing)},
    {typeof(IBar), typeof(BarThing)},
    ...
}

foreach(var entry in types)
{
    var castNode = node as entry.Key;
    return Activator.CreateInstance(entry.Value);
}

Naturally, it doesn't work: The type or namespace name 'entry' could not be found (are you missing a using directive or an assembly reference?). Can you help? Is this sort of thing even possible in C#?

Upvotes: 2

Views: 576

Answers (3)

Garvin
Garvin

Reputation: 487

Without understanding clearly your purpose for wanting to return different types from the same operation it will be hard to help. Maybe a little background information into the problem you are trying to solve??

I will assume that since you are attempting to return them interchangeably that fooThing, BartThing and BazThing have the same interface. So I am assuming the following:

 public class FooThing : IMyOperations
 {

 }

 public class BarThing : IMyOperations
 {

 }

 public class BazThing : IMyOperations
 {

 }

You can define the relationship between the classes in another interface

 public interface IMyChoice
 {
      public bool IsSelected { get; }
      public IMyOperations GetWorker();
 } 

 public class ChoiceFoo : IMyChoice
 {


 }

 public class ChoiceBar : IMyChoice
 {

 }

 public class ChoiceBaz : IMyChoice
 {

 }

Now you can say

 foreach( var entry in choices)
 {
    if(entry.IsSelected)
    {
         return entry.GetWorker();
         //Can't remember if i need to break after return..doubt it
    }
 }

Upvotes: 0

Justin
Justin

Reputation: 86729

How about this?

foreach(var entry in types)
{
    if (node != null && entry.Key.IsAssignableFrom(node.GetType()))
    {
        return Activator.CreateInstance(entry.Value);
    }
}

The problem is that you are confusing generic type parameters with runtime types and in particular the Type class.

If you know what a type will be at compile time then you can use the generic Activator.CreateInstance<T>() method to create an instance of the underlying object - you can use things like type parameters so that this line of code doesn't need to know what the type is, for example:

T CreateObject<T>()
{
    return Activator.CreateInstance<T>();
}

However this just passes the buck. In order to call this method the value of the type parameter T must be supplied somewhere - either way the compiler must be able to resolve T to a type (rather than a variable or method).

Conversely the Type class encodes type information at runtime such as its name or the assembly that a type is declared in. Activator.CreateInstance also comes with an overload that allows you to supply an instance of Type:

object CreateObject(Type type)
{
    return Activator.CreateInstance(type);
}

In your case it looks like you don't know what the types will be at compile time, so you will be mostly working with the Type class - you can use typeof(MyClass) to get an instance of the the corresponding Type for a class known at runtime, and myObject.GetType() to get type information for an object at runtime.

Upvotes: 3

Akash Kava
Akash Kava

Reputation: 39916

var types = new Dictionary<Type, Type>
{
    {typeof(IFoo), typeof(FooThing)},
    {typeof(IBar), typeof(BarThing)},
    ...
}

foreach(var entry in types)
{
    if(entry.Key.IsAssignableFrom(node.GetType()))
       return Activator.CreateInstance(entry.Value);
}

return null;

Upvotes: 2

Related Questions