Invoking delegates in a base constructor

J

Joel

Here's a tricky one for all the .NET heads.

I have a base class B with a constructor and an initialization method:

public B()
{
Initiliaze();
}

private void Initialize()
{
//enumerate through a farily large list
}

I have a derived class D that implements the default constructor and would
like to do something to each item in the list that Initialize enumerates.
OK, let's add an event handler to B!

protected delegate void OnEnumHandler(object obj);
protectd event OnEnumHandler OnEnum;

then in Initialize for each item I iterate through I can do:

if(OnEnum!=null)
OnEnum(item);

The problem is my constructor in D looks like:

public D() : base()
{
this.OnEnum+=new OnEnumHandler(My_OnEnum);
}

Now D's My_OnEnum will get called, right? Wrong. Because my base constructor
gets called before I have a chance to add my event handler.

So then I thought, just pass in the event handler as part of the base
constructor, e.g.: protected B(OnEnumHandler). Well, B compiles fine but
when in D when I do

protected D() : base(new OnEnumHandler(OnEnum))

it chokes with "An object reference is required for the nonstatic field,
method, or property 'My_OnEnum(object)'" because my object doesn't have life
yet.

I'm stumped. Any ideas? TIA

</joel>
 
J

Jon Skeet [C# MVP]

Joel said:
Here's a tricky one for all the .NET heads.

I have a base class B with a constructor and an initialization method:

I'm stumped. Any ideas? TIA

I would suggest using a different approach, to be honest. This kind of
thing tends to create problems - if you do things with the instance
before it's gone through its constructor, it tends to be problematic.
That said, you *could* have a virtual method which you call in
Initialize instead of calling the event handler. Indeed, you could
follow the normal .NET convention of having a virtual OnEnum method
which raises the Enum event; you would then override it in your derived
class to call base.OnEnum(parameter) and then do any processing
appropriately. Just be aware that your constructor hasn't been run at
that point!
 
J

Joel

Jon,

Yeah, I had thought about that but the problem is you break encapsulation by
putting the derived class in charge of making sure that Initialize() gets
appropriately called.

One way around I fournd was to make my OnEnum static. That way it's
available even without an instance of D. I have to pass the reference to
OnEnum in my constructor, e.g. public D() : base(new OnEnumHandler(OnEnum))
which is a little clunky but works. The caveat here is that only works if
you handler can be static. In my case it can so I think I found my
workaround but I can see getting bit by it again when I have a handler that
has instance data.

Thanks.
 
J

Jon Skeet [C# MVP]

Joel said:
Yeah, I had thought about that but the problem is you break encapsulation by
putting the derived class in charge of making sure that Initialize() gets
appropriately called.

Not with the latter solution - the base constructor could still call
Initialize() and Initialize() could call the virtual OnEnum method. I
personally don't like calling virtual methods from constructors though
- I think constructors should do as little as possible.

One way round this is to use a factory pattern, of course.
 
J

Joel

That won't work because since you're calling the base constructor explicitly
(public D() : base()), the base class is executing in it's own object space
and won't see any virtual methods. Also D is not even instantiated yet when
Initialize() gets called in the base.

I do agree with you about doing little in a constructor and I'm trying to
find a better way to implement it but I'm not having much luck other than to
force the user of the object to explicity call Initialize() which is a
little clunky.

I've just started looking into patterns and don't know them well enough to
understand how to solve this with the factory pattern but I have the "gang
of four" book (Gamma, Helm, Johnson, Vlissides) so I look into it. Thanks
for the tip.
 
J

Jon Skeet [C# MVP]

Joel said:
That won't work because since you're calling the base constructor explicitly
(public D() : base()), the base class is executing in it's own object space
and won't see any virtual methods.

Wanna bet?

using System;

class Base
{
public Base()
{
VirtualMethod();
}

protected virtual void VirtualMethod()
{
Console.WriteLine ("Base.VirtualMethod");
}
}

class Derived : Base
{
protected override void VirtualMethod()
{
Console.WriteLine ("Derived.VirtualMethod");
}
}

class Test
{
static void Main()
{
new Derived();
}
}
Also D is not even instantiated yet when Initialize() gets called in the base.

It's instantiated, just not initialised - all the memory for the object
has been allocated, but the constructor hasn't run.
 
J

Joel

mea culpa.

Your'e not going to beleive this (well if you been programming for most of
your life you will). I tried using the virtual method early on and was
tracing my code into the base class VirtualMethod. But when I tried yours it
workd. I went back to mine and, after about an hour, I realized that I was
inadvertantly executing some other code I had that was instatinatng the base
class directly. So when I saw the debugger trace into the VirtualMethod I
was seeing the direct instantiation of Base but thought I was seeing the
derived class. Arggghhhh.

Thanks for your help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top