2

I have two classes:

  class Car<T>
{
    public string color { get; set; }
    public virtual T features { get; set; }
    public virtual void TestDrive();
}
class Toyota : Car<ToyotaFeatures>
{
    public override ToyotaFeatures features { get; set; }
    public override void TestDrive()
    {
        //Logic here...
    }
}

Now i have a full string class name: "MySol.MyProj.Toyota"
I want to instantiate a class by my string name and then run TestDrive().
The problem is that when I try to run Activator.CreateInstance(null, "MySol.MyProj.Toyota");
I cannot cast it to a base class and run testDrive because it is expecting ToyotaFeatures class to be passed to it. but I just want to run TestDrive() only having a string class name.

And I don't want to cast it to specific type. only to base type so it can decide which TestDrive() to run based on the provided string.

user194076
  • 8,787
  • 23
  • 94
  • 154
  • 1
    Why do you want to cast the instance to the base class? Don't you want to run the TestDrive method on your instance? E.g., myInstance.TestDrive() – Brenda Bell Oct 16 '12 at 12:27

4 Answers4

3

For that purposes interfaces are extra good.

interface ITestDrivable
{
    void TestDrive();
}

Your abstract class implements the interface:

class Car<T> : ITestDrivable
{
    public string Color { get; set; }
    public virtual T Features { get; set; }
    public abstract void TestDrive() { }
}

And casting comes very easy:

ITestDrivable car = (ITestDrivable)Activator.CreateInstance(null, "MySol.MyProj.Toyota");
car.TestDrive();
AgentFire
  • 8,944
  • 8
  • 43
  • 90
  • That looks awesome. Can you also explain please how do I pass generic dataRow to a constructor so I can populate color and other properties? – user194076 Oct 16 '12 at 12:50
  • Can I do this inside my derived class? The thing is, I have a generic method, called: convertToObject(DataRow dr) which returns object of myType, but I'm not sure how to use it in my constructor. I was thinking about something like: this = ConvertToObject(dr) but this is not allowed. – user194076 Oct 16 '12 at 12:52
  • Or maybe I ca do this inside TestDrive(DataRow dr) method somehow. – user194076 Oct 16 '12 at 12:55
  • @user194076 First of all, you cannot do the `this = something` thing. Because `this` is readonly keyword. – AgentFire Oct 16 '12 at 13:23
  • @user194076 Second, I do not completely understand what you are trying to do. – AgentFire Oct 16 '12 at 13:24
  • What I want to do is, before running TestDrive I need to instantiate an object. and i have a method that returns that object of type T. – user194076 Oct 16 '12 at 13:27
  • so, in my Toyota I have a constructor Like this: Toyota(DataRow dr) { Toyota t = GetObject(dr); } I can do it manually like this: this.Color = dr["Color"].ToString(); but thought other ways are possible. Ideally I'd like to have a constructor in Car class that automatically casts to a selected derived object – user194076 Oct 16 '12 at 13:31
  • So, I guess this is not possible. http://stackoverflow.com/questions/5398904/deserialize-to-self and I cannot define factory method because then my interface will be expecting type T for an object and I won't be able to cast generic instance. – user194076 Oct 16 '12 at 13:40
  • You cannot write in `this` keyword, instead, consider copying all the properties to `this` from some other object. – AgentFire Oct 16 '12 at 13:46
3
Type type = Type.GetType("MySol.MyProj.Toyota");

var obj = Activator.CreateInstance(type);
type.GetMethod("TestDrive").Invoke(obj, null);
L.B
  • 114,136
  • 19
  • 178
  • 224
0

Would that make any sense:

abstract class CarFeatures { }

class ToyotaFeatures : CarFeatures { }

abstract class Car
{
    public string color { get; set; }
    public virtual void TestDrive() { }
}

abstract class Car<T> : Car where T : CarFeatures
{
    public virtual T features { get; set; }

    public Car(T _features)
    {
        features = _features;
    }
}

class Toyota : Car<ToyotaFeatures>
{
    public override ToyotaFeatures features { get; set; }

    public Toyota(ToyotaFeatures _features) : base(_features) { }

    public override void TestDrive()
    {
        //Logic here...
    }
}

More than once, I derived my generic class from a non-generic one to put all my common methods and fields that were not generic-dependant, giving me an easy access to them.

ToyotaFeatures features = new ToyotaFeatures();
Car car = (Car)Activator.CreateInstance(typeof(Toyota), new object[] { features });
car.TestDrive();

However, if someone has a better solution, I would be interrested too.

EDIT: As said above, interface are cool, but my rule of thumb is "Don't create an interface if you use it for only one class". But it works.

RE-EDIT: The problem was instanciating a generic object with a collection of params.

LightStriker
  • 19,738
  • 3
  • 23
  • 27
  • I cannot cast to Toyota because I only have string name of a class. It can be Honda as well. – user194076 Oct 16 '12 at 12:53
  • Why would you cast to Toyota if you only want to call TestDrive? Is there a special method in Toyota that you want to access? EDIT: AH! I think I see where you're going with this. You are creating a new object and you're having an hard time populating it's fields on initialization. – LightStriker Oct 16 '12 at 12:59
  • There you go. Activator.CreateInstance allows you to pass the params required by a Constructor. If your base class define a constructor that requires the CarFeatures, you can set them on the creation of your object. From that point, your object should be correctly setup to call its methods. – LightStriker Oct 16 '12 at 13:10
0

You should implement the interface IConvertible in your classes and then use the method Convert.ChangeType(..). Code Sample could be:

   // hords the created object
   object createdByActivator = ....;
   Car objectAsCar = Convert.ChangeType(createdByActivator, Type.GetType("MySol.MyProj.Toyota"

)

Mcolli
  • 26
  • 2
  • that `Car objectAsCar` thing in your code it his issue. He cannot just write `Car`, its actually `Car<>`, therefore, a generic class. – AgentFire Oct 16 '12 at 12:48