3

Greetings fellow members, the situation in question is such:

public abstract class BaseClass
{
    protected BaseClass()
    {
        // some logic here
    }

    protected BaseClass(Object parameter) : this()
    {
        // some more logic
    }
}

public class Descendant : BaseClass
{
   // no constructors
}

I'm trying to call Activator.CreateInstance on the Descendant class, but no constructor is found.

Do I need to explicitly define it on the Descentant class?

The bindings I've used are these: BindingFlags.Instance | BindingFlags.NonPublic

Note 1: I'm calling AppDomain.CreateInstanceAndUnwrap() in reality, if it should have some influence.

 domain.CreateInstanceAndUnwrap(path, typeName, false, BindingFlags.Instance |
     BindingFlags.NonPublic, null, new[] { parameter }, null, null);

Note 2: If I explicitly define the protected constructor in the Descendant class then it works, but I'd like to avoid this if possible.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
SmartK8
  • 2,616
  • 1
  • 29
  • 37
  • The problem is constructors aren't actually inherited, so searching the derived type for them is (as you've discovered) unproductive. – dlev Apr 10 '12 at 22:33
  • I know that, but don't want to force other programmers to implement that one default constructor over and over (I hate that also). – SmartK8 Apr 10 '12 at 22:35
  • If you know that, then you also know you need a different approach. The constructor you want to call doesn't exist! – dlev Apr 10 '12 at 22:37
  • Well I got it on the Descendant class initially (as it should be), but then I've got in to a business of trying to remove the need for that constructor in all the descendants. I'm aware that "Not possible!" is a possible answer, but I wanted to check it first. – SmartK8 Apr 10 '12 at 22:45

2 Answers2

4

You cannot use Activator.CreateInstance, but in Full Trust environments you should be able to locate and call the constructor directly by reflection.

Actually, your Descendant class automatically provides a public constructor that does a pass-through to the protected constructor of the base class. This should work just fine:

Activator.CreateInstance(typeof(Descendant))

Okay, so now I realize that you're trying to invoke the non-default constructor. Unfortunately for you, this is simply not allowed. Invoking the constructor on the base class directly won't work because the base class is abstract. You need to have a constructor (either auto-generated or explicitly defined) on the descendant class in order to construct an object.

You could create a pass-through constructor on the Descendant class, but your comments make it sound like you're trying to avoid forcing implementers to pass a value through in their constructor. What you probably really want is an initialization method that you can call after constructing the object:

// Define other methods and classes here
public abstract class BaseClass
{
    protected BaseClass()
    {
        // some logic here
    }

    protected Object Parameter {get; private set;}    

    public virtual void Initialize(Object parameter)
    {
        Parameter = _parameter;
        // some more logic
    }
}
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • But can it be achieved within AppDomain, can I somehow unwrap constructor, and the invoke it? – SmartK8 Apr 10 '12 at 22:36
  • That's not actually true. `CreateInstance` will specifically look for protected constructors and call them if you instruct it to. The issue here is that the derived class doesn't actually *have* any such constructor, protected or otherwise. – dlev Apr 10 '12 at 22:36
  • Updated my answer. In this case, the descendant class has a default public constructor. – StriplingWarrior Apr 10 '12 at 22:38
  • @StriplingWarrior Sure, but the OP is looking for the version with a parameter (which does not exist.) – dlev Apr 10 '12 at 22:40
  • @dlev: Thanks for clarifying. Now I see why she was having trouble. – StriplingWarrior Apr 10 '12 at 22:41
  • Ok that's a good start, that works. But that would seriously mess-up my API, because that interface would have to include some method to pass that parameter later on. I would really like to pass it in constructor? Any chance? :D – SmartK8 Apr 10 '12 at 22:41
  • It would be nice to be able somehow find constructor, then invoke it, but all this in an environment having type in a different AppDomain. o_O – SmartK8 Apr 10 '12 at 22:42
  • 1
    @SmartK8 While it sounds like you don't want to do this, you may consider passing the parameter to an `Initialize(param)` method that is defined as `virtual`/`abstract` in the base class. Without knowing more about what your design looks like and what your goals for the API are, it's difficult to give much more advice. – dlev Apr 10 '12 at 22:45
  • Well, it is a plug-in architecture (as one would expect with the AppDomains :), so sending this parameter via Initialize (yeah, I got exactly that method, no params at this time) sounds OK. StriplingWarrior's solution is quite fine, but if it were possible to go that one step further, it would be perfect. I'll give it few minutes, if someone will come up with anything :) – SmartK8 Apr 10 '12 at 22:50
  • @SmartK8: How about my updated code? The descendant class could access the parameter via the protected property. – StriplingWarrior Apr 10 '12 at 22:51
  • It's an OK solution, just one question before I'll mark it. Would it make any difference, if I'll make the base class non-abstract? – SmartK8 Apr 10 '12 at 22:54
  • @SmartK8: Yes, in the sense that you would be able to invoke the constructor via reflection. But you would no longer be instantiating the descendant type: you would instead be instantiating the base type. I doubt that's what you want. – StriplingWarrior Apr 10 '12 at 23:01
  • Uh-huh, true. Ok, thanks. Just for anyone who'll be interested in the future: I'll CreateInstance via default (automatic) constructor, and then I'll pass the parameter (TransparentProxy to PluginManager really) via Initialize method. This way there's no need for any constructor, and also I'll be able to hide the Initialize method on the abstract class (because Initialize method in on an interface really). – SmartK8 Apr 10 '12 at 23:05
1

What it seems like you want here is the builder pattern, rather than constructors. It's quite simple- you define a class (with a default constructor) that "builds" the class you want. Something like this (note: untested code ahead):

public abstract class BaseBuildable : MarshalByRefObject {
   public String Foo { get; internal set; }
}

public class DerivedBuildable : BaseBuildable { }

public class BuildableBuilder : MarshalByRefObject {
   private String _foo;
   public BuildableBuilder WithFoo(String foo) { _foo = foo; return this; }
   public TBuildable Build<TBuildable>() where TBuildable : BaseBuildable, new() {
       return new TBuildable { Foo = _foo; }
   }
}

// Used so:
var builder = domain.CreateInstanceAndUnwrap(.. // yadda yadda, you want a BuildableBuilder
var buildable = builder.WithFoo("Foo, yo").Build();
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • OK, but I see that DerivedBuildable has still a protected constructor? (which I'm trying to get rid off actually) – SmartK8 Apr 10 '12 at 23:11
  • Right, updated so it should work. The constructors were parameterless- just there to show I was using default constructors – Chris Shain Apr 10 '12 at 23:12
  • It looks reasonable, I'll up-vote you, and leave the answer to StriplingWarrior, if it is OK with you?. I'm sleepy (Europe), I'll resolve this issue tomorrow. – SmartK8 Apr 10 '12 at 23:21