Here is one of the perms and combs I have tried that may make things
more clear as to what I am trying to do:
Note that this example has the same problem as your previous
misunderstanding of what I was trying to suggest (and which Jon tried to
clarify).
When I suggested that you need to use a concrete type for the type
parameter eventually, my point wasn't that you should just put it in where
convenient. You still need to comply with the chain of generic
declarations.
To explain the error you're getting now:
[...]
if (this.UnsubscribeDelegatesFromBinOwner != null)
UnsubscribeDelegatesFromBinOwner(this);
COMPILER ERROR:
cannot convert from 'XmlPIDObjCollectionBase<T>' to
'XmlPIDObjCollectionBase<XmlPIDObjBase>'
This is because generics don't allow covariance. The only thing that can
successfully be cast to an "XmlPIDObjCollectionBase<XmlPIDObjBase>" is
either an instance of that class, or one that inherits _from that class_..
Note that something that inherits from
XmlPIDObjCollectionBase<XmlPIDObjBase> will be declared like this:
class SomeClass : XmlPIDObjCollectionBase<XmlPIDObjBase>
or even:
class SomeClass<T> : XmlPIDObjCollectionBase<T>
where T _is_ XmlPIDObjBase when SomeClass is actually declared. It's not
sufficient for T to simply inherit XmlPIDObjBase in that case; it would
actually have to be XmlPIDObjBase.
For more details on that, just Google this newsgroup for "generic
covariance". There's been lots of discussion on it in previous threads.
Now, as far as what you're actually doing, you still haven't really
explained _what_ you want to do. You keep posting code that illustrates
_how_ you've tried to do it, but since none of those code examples
actually work, they really aren't all that good at illustrating the "what".
If I have successfully interpreted your intent, it's my opinion that you
don't want to use a delegate at all. An interface may be a better way of
dealing with the situation, and it's possible that you could write the
code just to call the method in question directly. I think that using a
delegate is just making the code more difficult to implement, and it will
likely make the code harder to understand later.
However, if you really feel tied to a delegate-based implementation, maybe
something like this example would work for you:
using System;
namespace TestGenericSubscriber
{
class Program
{
class MyBase
{
}
class MyOwned<T> where T : MyBase
{
public delegate void UnsubscribeDelegate(MyOwned<T> owned);
private UnsubscribeDelegate _unsubscribe;
public MyOwned(UnsubscribeDelegate unsubscribe)
{
Console.WriteLine("creating MyOwned instance");
_unsubscribe = unsubscribe;
}
public void Remove()
{
Console.WriteLine("removing MyOwned instance");
if (_unsubscribe != null)
{
_unsubscribe(this);
}
}
}
class MyOwner
{
MyOwned<MyBase> _owned;
public void MakeOwned()
{
Console.WriteLine("making _owned");
// This next line is probably the most important for you
// to understand. In addition to the declaration of the
// local variable in which the reference to the
// instance is stored, this is the only other place
// that an actual type is supplied for the generic type
// parameter. In addition, it is at this point that you
// _must_ provide an actual type, since the class and
// method containing this code is _not_ generic, and so
// have no generic type parameters to take the place of
// an actual type.
_owned = new MyOwned<MyBase>(UnsubscribeHandler<MyBase>);
}
public void RemoveOwned()
{
Console.WriteLine("removing _owned");
_owned.Remove();
_owned = null;
}
private void UnsubscribeHandler<T>(MyOwned<T> owned) where T :
MyBase
{
Console.WriteLine("unsubscribe handler");
}
}
static void Main(string[] args)
{
MyOwner owner = new MyOwner();
owner.MakeOwned();
owner.RemoveOwned();
Console.ReadLine();
}
}
}
Note that rather than passing an instance of the class containing the
method to the constructor of the "owned" class, I just pass the delegate
instance itself. There is no reason to use a delegate if you are passing
the instance of the class, since if you have a reference to the instance,
and the method you're trying to call is accessible, you can just call the
method directly rather than going through the delegate.
So a delegate-based solution only makes sense if you don't have the
instance to the class containing the called method. Thus, this example
was written that way.
Because of this change, you'll note that the "subscribe" method (which
presumably would have been called from the code where the owned object is
instantiated) just isn't even there anymore. Rather than having the
constructed class call back to the owning class, the owning class just
passes the necessary delegate instance. No calling back to the owning
class, thus no need for the subscribe method at all.
On the assumption that your owned class can only have one owner at a time,
I've changed the syntax used somewhat, by not using the multicast aspect
of the delegate at all. It's simply assigned and cleared. But there's no
reason you couldn't use the multicast syntax if you wanted to. It would
follow basically the same organization (and the same requirement that you
at some point use an actual class to define what type is being used to
create the generic type instance).
Of course, as I mentioned before, it seems to me that if you have the
luxury of passing an instance of the class containing the called method,
then you might as well just store that instance reference and call the
method directly when needed. That's the simplest way to write the code,
and it will be the most understandable. This is especially true if you
really do have the "owner/owned" relationship that your example implies
and there will only be one instance "subscribed" to the owned object at a
time.
I am stumped and ready to give up but I can not believe that what is
seemingly a very simple thing to do can not be done. Please help. I
am past my meltdown but I also have three holes in my office walls and
am now most likely going to miss a deadline.
For what it's worth, your frustration seems to have extended to preventing
you from posting a good question. It's not that the information you seek
isn't worthwhile. It's that how you present that question has been poor..
One of the most important things, in all endeavors, is Douglas Adams's
famous admonition: DON'T PANIC! That's true here too. If you need to
blow off steam, go do that (but I don't recommend punching holes in your
walls). If you need an answer to a question, ask the question. But don't
ask a question while you still need to blow off steam. The first step to
getting anything done is to calm down. Otherwise nothing will work.
Pete