Aaron Powell
Aaron Powell

Reputation: 25099

Upcasting in .NET using generics

So I've got a class like this:

public class A { 
  internal A(int i) { ... }
  public int Foo { get; set; }
}

This class is then inherited by a bunch of generated classes, eg:

public class B : A {
  ...
}

The int based constructor isn't exposed to the inherited class (for design reasons I don't want it exposed). In my library which holds the definition for class A I've got a method like this:

public T Load<T>() where T : A {
  //do some stuff, create an instance of T from an int, then return it
}

And then I'd use it like this:

B b = Helper.Load<B>();

Since the constructor I want to use isn't exposed to class B when I do typeof(T).GetConstructor(typeof(int)) I don't get the constructor back, so I want thinking that I'd do this:

return (T)new A(/*some int */);

But that gives me a runtime error System.InvalidCastException, that I can't cast a type A to type B.

How do I go about achieving this upcasting?

Upvotes: 1

Views: 5382

Answers (4)

Cecil Has a Name
Cecil Has a Name

Reputation: 4992

You can just use default constructors so you can instantiate objects of type T with the new() constraint. Then class A can have a virtual (or abstract to your liking) method that takes an int as an argument and initializes the object after the constructor has run.

public class A {
    internal A() { }
    internal Initialize(int i) { Foo = i; }
    public int Foo { get; set; }
}

public class B : A { 
    internal B() { }
}

...

public T Load<T>() where T : A, new() {
    var ret = new T();
    ret.Initialize(i);
    return ret;
}

If you intend some sort of factory pattern, you don't need to hesitate initializing parts of an object outside the constructor call as long as it is done before you return the object to the caller's control.

Upvotes: 4

Brijesh Mishra
Brijesh Mishra

Reputation: 2748

You cant do this, change your design.

Upvotes: 0

AaronLS
AaronLS

Reputation: 38365

You can't upcast A to B in your example, because:

return (T)new A(/*some int */);

Instantiates an A, which is not a B. Just because a "B is an A" does not mean "A is a B". You would have to first instantiate a B, cast it to an A, do what you want, and then upcast it back to a B.

I'm not sure if this is will compile, but you could try this:

T blah = new T(5); //this means your B will need to implement a int constructor
A blah2 = (A)blah;
//operate on A specific operations in blah2
T upcasted = (T)blah2;
//alternatively
T upcasted = blah2 as T;

Consider refactoring your contructor such that you initialize the integer as a property, instead of a parameter of the constructor. I strive to have default(no parameters) contructors so that generic code can instantiate the class easily.

Upvotes: 1

Philippe
Philippe

Reputation: 4051

From what I understood, T derives from A, so you can't cast A to T.

Upvotes: 1

Related Questions