Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# delegate and abstract class

Tags:

c#

I currently have 2 concrete methods in 2 abstract classes. One class contains the current method, while the other contains the legacy method. E.g.

// Class #1
public abstract class ClassCurrent<T> : BaseClass<T> where T : BaseNode, new()
{
    public List<T> GetAllRootNodes(int i)
    {
      //some code
    }
}

// Class #2
public abstract class MyClassLegacy<T> : BaseClass<T> where T : BaseNode, new()
{
    public List<T> GetAllLeafNodes(int j)
    {
      //some code
    }
}

I want the corresponding method to run in their relative scenarios in the app. I'm planning to write a delegate to handle this. The idea is that I can just call the delegate and write logic in it to handle which method to call depending on which class/project it is called from (at least thats what I think delegates are for and how they are used).

However, I have some questions on that topic (after some googling):

1) Is it possible to have a delegate that knows the 2 (or more) methods that reside in different classes? 2) Is it possible to make a delegate that spawns off abstract classes (like from the above code)? (My guess is a no, since delegates create concrete implementation of the passed-in classes) 3) I tried to write a delegate for the above code. But I'm being technically challenged:

    public delegate List<BaseNode> GetAllNodesDelegate(int k);
    GetAllNodesDelegate del = new GetAllNodesDelegate(ClassCurrent<BaseNode>.GetAllRootNodes);

I got the following error:

An object reference is required for the non-static field, method, property ClassCurrent<BaseNode>.GetAllRootNodes(int)

I might have misunderstood something... but if I have to manually declare a delegate at the calling class, AND to pass in the function manually as above, then I'm starting to question whether delegate is a good way to handle my problem.

Thanks.

like image 531
BeraCim Avatar asked Mar 17 '10 07:03

BeraCim


2 Answers

The way you're attempting to use delegates (constructing them with new, declaring a named delegate type) suggests that you're using C# 1. If you're actually using C# 3, it's much easier than that.

Firstly, your delegate type:

public delegate List<BaseNode> GetAllNodesDelegate(int k);

Already exists. It's just:

Func<int, List<BaseNode>>

So you don't need to declare your own version of it.

Secondly, you should think of a delegate as being like an interface with only one method in it, and you can "implement" it on the fly, without having to write a named class. Just write a lambda, or assign a method name directly.

Func<int, List<BaseNode>> getNodesFromInt;

// just assign a compatible method directly
getNodesFromInt = DoSomethingWithArgAndReturnList;

// or bind extra arguments to an incompatible method:
getNodesFromInt = arg => MakeList(arg, "anotherArgument");

// or write the whole thing specially:
getNodesFromInt = arg =>
    {
        var result = new List<BaseNode>();
        result.Add(new BaseNode());
        return result;
    };

A lambda is of the form (arguments) => { body; }. The arguments are comma-separated. If there's only one, you can omit the parentheses. If it takes no parameters, put a pair of empty parentheses: (). If the body is only one statement long, you can omit the braces. If it's just a single expression, you can omit the braces and the return keyword. In the body, you can refer to practically any variables and methods from the enclosing scope (apart from ref/out parameters to the enclosing method).

There's almost never any need to use new to create a delegate instance. And rarely a need to declare custom delegate types. Use Func for delegates that return a value and Action for delegates that return void.

Whenever the thing you need to pass around is like an object with one method (whether an interface or a class), then use a delegate instead, and you'll be able to avoid a lot of mess.

In particular, avoid defining interfaces with one method. It will just mean that instead of being able to write a lambda to implement that method, you'll have to declare a separate named class for each different implementation, with the pattern:

class Impl : IOneMethod
{
    // a bunch of fields

    public Impl(a bunch of parameters)
    {
        // assign all the parameters to their fields
    }

    public void TheOneMethod()
    {
        // make use of the fields
    }
}

A lambda effectively does all that for you, eliminating such mechanical patterns from your code. You just say:

() => /* same code as in TheOneMethod */

It also has the advantage that you can update variables in the enclosing scope, because you can refer directly to them (instead of working with values copied into fields of a class). Sometimes this can be a disadvantage, if you don't want to modify the values.

like image 91
Daniel Earwicker Avatar answered Sep 28 '22 01:09

Daniel Earwicker


You can have a delegate that is initialized with references to different methods depending on some conditions.

Regarding your questions:
1) I'm not sure what you mean under "knows". You can pass any method to the delegate, so if you can write method that "knows" about some other methods than you can do a similar delegate.
2) Again, delegates can be created from any method that can be executed. For example if you have an initialized local variable of type ClassCurrent<T> you can created delegate for any instance method of type ClassCurrent<T>.
3) Delegate can call only the method that actually can be called. I mean that you cannot call ClassCurrent.GetAllRootNodes because GetAllRootNodes is not a static method, so you need an instance of the ClassCurrent to call it.

The delegate can stay in any class that has access to the ClassCurrent and MyClassLegacy.

For example you can create smth like:

class SomeActionAccessor<T>
{
    // Declare delegate and fied of delegate type.
    public delegate T GetAllNodesDelegate(int i);

    private GetAllNodesDelegate getAllNodesDlg;

    // Initilaize delegate field somehow, e.g. in constructor.
    public SomeActionAccessor(GetAllNodesDelegate getAllNodesDlg)
    {
        this.getAllNodesDlg = getAllNodesDlg;
    }

    // Implement the method that calls the delegate.
    public T GetAllNodes(int i)
    {
        return this.getAllNodesDlg(i);
    }
}

The delegates can wrap both static and instance method. The only difference is that for creation delegate with instance method you need instance of the class who owns the method.

like image 42
Andrew Bezzub Avatar answered Sep 28 '22 00:09

Andrew Bezzub