Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Thursday, August 20, 2009 3:33 PM
Hello,
Thanks for reading my post and helping me with figure this out. Can I have an abstract class which contains a delegate? For example, let's say I have...
Pet (abstract class)
delegate void speak( Pet aPet, string word )
Dog : Pet
void speak( string word ) { }
Cat : Pet
void speak( string value ) { }
I want to be able to use the same delegate to invoke speak() on Dog and Cat. Can this be done?
Thanks for everyone's help!
All replies (8)
Thursday, August 20, 2009 6:39 PM âś…Answered
Reed,
Hmm...thanks for the suggestion. The last thing I want to do is complicate the code. So, I guess I'm out of luck. I was trying to avoid a long case statement by using the delegate (an attempt to make my code dynamic). Oh well...
If that's your goal, I'd recommend putting the "switch" statement into your configuration file. You're already using reflection in your code - it'd be very easy to have your configuration specify "Key" == "PropertyName" + "Property Type", and just set the property directly via reflection at runtime.
That would let you have a single code block to handle any property, on any class type.
Reed Copsey, Jr. - http://reedcopsey.com
Thursday, August 20, 2009 3:59 PM
You ~can~ have delegates (as a member field or property) in abstract classes. Abstract classes can contain anything that normal, concrete implementation classes can contain - they just can't be instanced, and must be subclassed.
However, in your case, your delegate declaration for speak does not match the methods you specified. The delegate requires a Pet to be passed in as the first argument. Normally, for something like this, you'd use abstract/virtual methods:
abstract class Pet {
public abstract void Speak(string word);
}
public class Dog : Pet {
public override void Speak(string word ) { ... }
}
public class Cat : Pet {
public override void Speak(string word ) { ... }
}
Then, you can just do:
Pet aPet = ... // create a pet
aPet.Speak("Say this!");
This could be wrapped into delegates in one way or another, but it becomes less obvious. What are you trying to accomplish, exactly? Why does the standard approach not work in your case? We might be able to help you understand the best approach.
Reed Copsey, Jr. - http://reedcopsey.com
Thursday, August 20, 2009 4:36 PM
Hi Reed,
Thanks for your feedback. Well, I was hoping I would do better at getting my point across. :( Let me try explaining it this way instead. What I am trying to do is read a file (two different) files and set the values on two different classes. I'm trying to use a generic delegate (for strings, one for bool, etc.) to set the values on these two classes. Take a look at this (I hope its a better example):
Thanks!
-sasims
File1:
flower=rose; petals=true; color=red
File2
tree=oak; leaves=green; isTall=true;
delegate void FileDelegate( File f, string a ) //used to set strings
delegate void FileDelegate( File f, bool a ) //used to set booleans
abstract class File {
...
}
class File1 : File {
void setFlower( string value ) { ... }
void setPetals( bool value ) {...}
}
class File2 : File {
void setTree( string value ) { ... }
void setIsTrue( bool value ) {...}
}
Thursday, August 20, 2009 4:54 PM
Unfortunately, you can't write the delegates the way you're showing it above. If you specify a delegate like so:
delegate void FileDelegate( File f, string a ); //used to set strings
You would need to use a method that was defined the same way:
class File1 {
void SetFlower( File f, string value); // Note the file member here!
}
That being said, you could potentially do something (although I think it's probably more effort than its worth) like the following:
delegate void SetFileStringDelegate( string str ); //used to set strings
delegate void SetFileBoolDelegate( bool b ); //used to set booleans
abstract class File {
protected abstract SetFileStringDelegate GetStringDelegate(string propertyName); // Need a way to look up the delegate
}
class File1 : File
{
protected override SetFileStringDelegate GetStringDelegate(string propertyName)
{
switch (propertyName)
{
case "flower":
return this.SetFlower;
// .. add case per property
}
}
private void SetFlower(string value) {... }
}
This type of architecture would work - but it's really complicated to follow. I personally think you'd be better off just having some internal method that took arguments like (string name, string value) and did the conversions in each class. This would be much easier to understand, and not require passing delegates around at all.
Reed Copsey, Jr. - http://reedcopsey.com
Thursday, August 20, 2009 5:01 PM
Sasims,
I don't see how that makes your intent any clearer.
Perhaps Reed might, but not me. I agree with Reed's points, too.
Who would be the consumer of your delegate? Pet?
I think you are unclear on what a delegate is and what function it serves. The actual word English "delegate" implies assigning duty to an outside entity or party. C# Delegates work pretty much the same way. You assign a target or method to them, which are added to their Invocation List. This is a common design practice when writing code for classes, delegating the actual implementation of a method to an outside class.
For example, we can do that with your Speak delegate, which I have renamed for clarity....
public delegate void SpeakAction(string word);
abstract class Pet
{
private SpeakAction speak;
public Pet(SpeakAction speakAction)
{
this.speak = speakAction;
}
public virtual void Speak(string word)
{
speak.Invoke(word);
}
}
class Dog : Pet
{
public Dog(SpeakAction speakAction)
: base(speakAction)
{
}
}
class Cat : Pet
{
public Cat(SpeakAction speakAction)
: base(speakAction)
{
}
}
class TestPet { public static void Method1() { Pet pet; pet = new Cat(PetSpeak); pet.Speak("Invoke the delegate through a cat."); pet = new Dog(PetSpeak); pet.Speak("Invoke the delegate through a dog."); } private static void PetSpeak(string word) { Console.WriteLine(word); } }
..........and many other ways. The point is that the actual implementation of Speak is left up to consumers. This can be done with all sorts of stuff that you do not know when the class is written. It also allows for code to be easily upgraded without re-writing the original types!
rudedog =8^D
Mark the best replies as answers. "Fooling computers since 1971."
Thursday, August 20, 2009 6:14 PM
Reed,
Hmm...thanks for the suggestion. The last thing I want to do is complicate the code. So, I guess I'm out of luck. I was trying to avoid a long case statement by using the delegate (an attempt to make my code dynamic). Oh well...
RudeDog,
But I am "assigning duty to an outside entity or party", the Builder class is invoking the methods (shown below).
So back to this example (modified to be more accurate), two text files as shown below (File1.txt and File2.txt):
File1: setFlower=rose; setPetals=true; setColor=red
File2: setTree=oak; setLeaves=green; isTall=true;
using System;
using System.Collections.Generic;
using System.Text;
namespace Test {
delegate void FileStringDelegate( File f, string a ); //used to set strings
delegate void FileBoolDelegate( File f, bool a ); //used to set booleans
public abstract class File { //...
}
public class File1 : File {
public void setFlower( string value ) { //...
}
public void setPetals( bool value ) { //...
}
}
public class File2 : File {
public void setTree( string value ) { //...
}
public void setIsTrue( bool value ) { //...
}
}
public class Builder {
public void buildFileObject() {
//...
string value = "rose"; //a value read from the text file: File1;
string key = "setFlower" //value read from the text file: File1;
//...
File1 f1 = new File1();
Delegate del = GetDelegateFor( "File1", key, "FileStringDelegate" );
del( f1, value ); //So here is where I get an Exception: Error binding to target method.
}
public Delegate Delegate GetDelegateFor(string classname, string methodName, string delegateType) {
Type type = asm.GetType(classname);
//...
Delegate toDelegate = null;
MethodInfo method = type.GetMethod(methodName.Trim(), BindingFlags.Public | BindingFlags.Instance);
if (method != null) {
if (delegateType.Equals("FileStringDelegate"))
toDelegate = Delegate.CreateDelegate(typeof(FileStringDelegate), method);
if (delegateType.Equals("FileBoolDelegate"))
toDelegate = Delegate.CreateDelegate(typeof(FileBoolDelegate), method);
}
return toDelegate;
}
}
}
I guess I will have to rethink this through unless this better examples helps??? :)
-sasims
Thursday, August 20, 2009 6:58 PM
I've never had a purpose to used reflection before. I'll give this approach a try instead.
Thanks Reed! You are a great help!
-sasims
Thursday, August 20, 2009 7:11 PM
That overload for CreateDelegate is looking for a static method.
You would need to supply an instance object to one of the overload to bind to a non-static method.
You also had a couple of syntax errors and other issues in your posted code.
Delegate and method signatures didn't match up.
I fixed them up......just the one method.....
delegate void FileStringDelegate( File f, string a ); //used to set strings
delegate void FileBoolDelegate( File f, bool a ); //used to set booleans
public abstract class File { //...
}
public class File1 : File {
public static void setFlower(File file, string value ) { //...
}
public void setPetals( bool value ) { //...
}
}
public class File2 : File {
public void setTree( string value ) { //...
}
public void setIsTrue( bool value ) { //...
}
}
public class Builder
{
public void buildFileObject()
{
//...
string value = "rose"; //a value read from the text file: File1;
string key = "setFlower"; //value read from the text file: File1;
//...
File1 f1 = new File1();
Delegate del = GetDelegateFor( "File1", key, "FileStringDelegate" );
del.DynamicInvoke ( f1, value ); //So here is where I get an Exception: Error binding to target method.
}
Assembly asm = Assembly.GetExecutingAssembly();
public Delegate GetDelegateFor(string classname, string methodName, string delegateType)
{
string nameSpace = this.GetType().Namespace + ".";
Type type = asm.GetType(nameSpace + classname); // requires Builder and target classes to be in same namespace!!!
//...
Delegate toDelegate = null;
MethodInfo method = type.GetMethod(methodName.Trim(), BindingFlags.Public | BindingFlags.Static );
if (method != null)
{
if (delegateType.Equals("FileStringDelegate"))
toDelegate = Delegate.CreateDelegate(typeof(FileStringDelegate), method);
if (delegateType.Equals("FileBoolDelegate"))
toDelegate = Delegate.CreateDelegate(typeof(FileBoolDelegate), method);
}
return toDelegate;
}
}
Mark the best replies as answers. "Fooling computers since 1971."