views:

294

answers:

6

How do I grab the Type of the inherited class and pass it into the base constructor of the class also inherited? See the code sample below:

// VeryBaseClass is in an external assembly
public abstract class VeryBaseClass
{
    public VeryBaseClass(string className, MyObject myObject)
    {

    }
}

// BaseClass and InheritedClass are in my assembly
public abstract class BaseClass : VeryBaseClass
{
    public BaseClass(MyObject myObject) :
        base(this.GetType().Name, myObject) // can't reference "this" (Type expected)
    {

    }
}

public class InheritedClass : BaseClass
{
    public InheritedClass(MyObject myObject)
    {

    }
}

The line base(typeof(this).Name, myObject) doesn't work because I can't reference this yet, as the object hasn't finished constructing and therefore doesn't exist.

Is it possible to grab the Type of the currently constructing object?

EDIT:

Corrected the sample as orsogufo suggested, but still doesn't work, as this is undefined.

EDIT 2:

Just to clarify, I want to end up with "InheritedClass" being passed into the VeryBaseClass(string className, MyObject myObject) constructor.

+3  A: 

Does GetType().Name not work?

pdr
No, because that implicitly uses "this" which you can't use to evaluate constructor chaining arguments.
Jon Skeet
Sorry, I didn't mean in a constructor chain, I meant that the constructor of VeryBaseClass could use GetType().Name rather than expecting it to be passed.
pdr
@pdr: That assumes that VeryBaseClass always wants the name of the current type; while that may be a reasonable design in some cases, it isn't always. I'm looking at this as a broader question of "what do you do when you want to use `this` in a constructor chain argument" - because you can't always push the functionality up the inheritance tree.
Jon Skeet
@pdr: Unfortunately, the `VeryBaseClass` cannot be modified at this point. I can only modify the other two classes :(
Codesleuth
Ok, I see your point. I was trying to solve the questioner's problem. I would suggest that if you want to use 'this' in a constructor chain argument, you should probably be thinking hard about a redesign.
pdr
@Codesleuth - ok, that would be a problem. I think you probably have to stick with strings :(. And have a word with the people who provided VeryBaseClass
pdr
@pdr: I've complained to them before, but to no avail. They left the company, with an application already built on top of the framework. Typical!
Codesleuth
@Codesleuth: I feel your pain, really I do
pdr
+3  A: 

A possible solution:

class VeryBase {
    public VeryBase(string name) {
        Console.WriteLine(name);
    }
}

class Base<T> : VeryBase where T : Base<T> {
    protected Base()
        : base(typeof(T).Name) {
    }
}

class Derived : Base<Derived> {
    public Derived() {
    }
}

class Program {
    public static void Main(params string[] args) {
        Derived d = new Derived();
    }
}
Paolo Tedesco
Sorry, you're right. this.GetType should have been my example. Alas, `this` is undefined.
Codesleuth
Ah, ok... well, I have updated my post with a possible (even if not very easy to use) solution...
Paolo Tedesco
@orsogufo: That becomes trickier if you want to add another layer of inheritance, and it's put the logic of using Type.Name into the base class instead of the derived class. (In the question, that logic is in the middle layer of the three.)
Jon Skeet
@Jon: thanks for the remark, I added the extra layer. By the way, I think your solution is better, this was just an idea :)
Paolo Tedesco
@orsogufo: Okay, now how do you further derive from Derived? You'd have to make that generic too... This is a problem with generics in general :(
Jon Skeet
+4  A: 

I've had exactly the same pain before now in the Google Wave Robot .NET API where I wanted to make the constructor pass in some values based on attributes. You can have a look at my solution in the code for the derived type and the base type. Basically I pass a delegate to the base constructor, and then call that delegate passing in "this" to the delegate. So in your case you'd have:

public VeryBaseClass(Func<VeryBaseClass, string> classNameProvider)
{
    this.name = classNameProvider(this);
}

and

public BaseClass() : base(FindClassName)
{
}

private static string FindClassName(VeryBaseClass @this)
{
    return @this.GetType().Name;
}

It's really ugly, but it works.

EDIT: This approach only works if you can change your base class constructor as shown; if you can't, I'm not sure it's actually feasible at all :(

Jon Skeet
I've never seen `@this` before, and seems to confuse Google. Does it just allow a method to be called with no parameters, and automatically place the current object in? (that's how I see it)
Codesleuth
No, the @ in front of it is just saying the compiler to consider 'this' as an identifier (to distinguish it from the keyword). Check this: http://msdn.microsoft.com/en-us/library/aa664670%28VS.71%29.aspx. You could replace '@this' with any parameter name.
Paolo Tedesco
`Argument '1': cannot convert from 'method group' to 'string'` I guess I should mention here that although my example shows just the one parameter being used by the base class, my actual implementation uses a few more. I'll update the question to reflect this.
Codesleuth
@CodeSleuth: Did you change the VeryBaseClass constructor as shown? If so, it shouldn't be expecting a string any more...
Jon Skeet
Ah, I've just seen that you don't control the base class. In that case this solution won't work for you - I'll edit my answer to show that.
Jon Skeet
Awww, you were so close! I surely can't be the first person who's had this problem, there must be a resolution (or explanation why there isn't) somewhere.
Codesleuth
@Downvoter: Care to comment?
Jon Skeet
A: 

Yet another option...

// VeryBaseClass is in an external assembly
public abstract class VeryBaseClass
{
    public VeryBaseClass(string className)
    {
        Console.WriteLine(className);
    }
}

// BaseClass and InheritedClass are in my assembly
public abstract class BaseClass : VeryBaseClass
{
    public BaseClass(string className)
        :
        base(className)
    {

    }
}

public class InheritedClass : BaseClass
{
    public InheritedClass()
        : base(typeof(InheritedClass).Name)
    {
    }
}
Paolo
This would be just the same as typing the class name in as a string. I wanted to avoid having to pass anything I don't have to into the `BaseClass` constructor from the `InheritedClass`.
Codesleuth
+2  A: 

If the ability to determine the inherited class's type at runtime is more important than performance you can take a look at the StackTrace to see where the constructor is being called from. You may need to code in more assumptions when calling GetInheritedClassName in this example:

public abstract class BaseClass : VeryBaseClass
{
    private static string GetInheritedClassName
    {
        get
        {
            // Get the current Stack
            StackTrace currentStack = new StackTrace();
            MethodBase method = currentStack.GetFrame(1).GetMethod();

            // 1st frame should be the constructor calling
            if (method.Name != ".ctor")
                return null;

            method = currentStack.GetFrame(2).GetMethod();

            // 2nd frame should be the constructor of the inherited class
            if (method.Name != ".ctor")
                return null;

            // return the type of the inherited class
            return method.ReflectedType.Name;
        }
    }

    public BaseClass(MyObject myObject) :
        base(GetInheritedClassName, myObject)
    {

    }
}

I cannot think of a great way to dynamically get the name of the inherited class without performance implications. I realize is what you wanted to avoid but if I needed to call a 3rd party assembly with these requirements I would just require each class to specify its own type:

public abstract class BaseClass : VeryBaseClass
{
    public BaseClass(string className, MyObject myObject) :
        base(className, myObject)
    {

    }
}

public class InheritedClass : BaseClass
{
    public InheritedClass(MyObject myObject) : base("InheritedClass", myObject)
    {

    }
}
sirchristian
I like the resourcefulness of this answer, but you're right about it being performance intensive. After reviewing my code, I can see that I don't construct many of these objects at once at any point in my code, so this is definitely viable. Definitely a +1 answer :)
Codesleuth
+1  A: 

Ah Hah! I found a solution. You can it do with generics:

public abstract class VeryBaseClass
{
    public VeryBaseClass(string className, MyObject myObject)
    {
        this.ClassName = className;
    }

    public string ClassName{ get; set; }
}
public abstract class BaseClass<T> : VeryBaseClass
{
    public BaseClass(MyObject myObject)
        : base(typeof(T).Name, myObject)
    {
    }
}

public class InheritedClass : BaseClass<InheritedClass>
{
    public InheritedClass(MyObject myObject) 
        : base(myObject)
    {

    }
}
Thomas
Oh my god, genius! I'll test it when I've finished butchering my TFS Source Control layout :)
Codesleuth
Excellent, it works! Although it means I have a lot of work to do to change my existing classes, but it will be worth it in the end. Thank you :)
Codesleuth