c#委托和抽象类

时间:2010-03-17 07:17:10

标签: c#

我目前在2个抽象类中有2个具体方法。一个类包含当前方法,而另一个类包含遗留方法。 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
    }
}

我希望相应的方法在应用程序的相对场景中运行。我打算写一个代表来处理这件事。我的想法是,我可以调用委托并在其中编写逻辑来处理调用哪个方法,具体取决于调用它的类/项目(至少我认为代理的用途以及如何使用它们)。

但是,我对该主题有一些疑问(经过一些谷歌搜索):

1)是否可以让代理知道驻留在不同类中的2个(或更多)方法? 2)是否可以使一个委托产生抽象类(如上面的代码)? (我的猜测是否定的,因为委托创建了传入类的具体实现) 3)我试图为上面的代码编写一个委托。但我受到技术挑战:

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

我收到以下错误:

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

我可能误解了一些东西......但是如果我必须在调用类中手动声明一个委托,并且如上所述手动传递函数,那么我开始质疑委托是否是一种好的方法来处理我的问题。

感谢。

5 个答案:

答案 0 :(得分:8)

您尝试使用委托(使用new构建它们,声明命名委托类型)的方式表明您使用的是C#1。如果您实际使用的是C#3,那么它比这一点。

首先,你的代表类型:

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

已经存在。它只是:

Func<int, List<BaseNode>>

所以你不需要声明你自己的版本。

其次,您应该将委托视为只有一个方法的接口,并且您可以动态“实现”它,而无需编写命名类。只需编写一个lambda,或直接指定方法名称。

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;
    };

lambda的格式为(arguments) => { body; }。参数以逗号分隔。如果只有一个,则可以省略括号。如果没有参数,请输入一对空括号:()。如果正文只有一个语句,则可以省略大括号。如果它只是一个表达式,您可以省略大括号和return关键字。在正文中,您几乎可以参考封闭范围内的任何变量和方法(除了ref / out参数到封闭方法)。

几乎不需要使用new来创建委托实例。并且很少需要声明自定义委托类型。对于返回值的代理使用Func,为返回Action的代理使用void

每当你需要传递的东西就像一个带有一个方法的对象(无论是接口还是类),然后使用委托,你就能避免很多混乱。

特别是,避免使用一种方法定义接口。它只是意味着不是能够编写lambda来实现该方法,而是必须为每个不同的实现声明一个单独的命名类,使用模式:

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
    }
}

lambda有效地为您完成所有这些工作,消除了代码中的这些机械模式。你只要说:

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

它还具有以下优点:您可以更新封闭范围中的变量,因为您可以直接引用它们(而不是使用复制到类的字段中的值)。如果您不想修改值,有时这可能是一个缺点。

答案 1 :(得分:1)

根据某些条件,您可以使用对不同方法的引用来初始化委托。

关于你的问题:
1)我不确定你在“知道”下的意思。您可以将任何方法传递给委托,因此如果您可以编写“知道”某些其他方法的方法,那么您可以执行类似的委托。
2)同样,可以从任何可以执行的方法创建委托。例如,如果您有一个类型为ClassCurrent<T>的初始化局部变量,则可以为ClassCurrent<T>类型的任何实例方法创建委托。 3)Delegate只能调用实际可以调用的方法。我的意思是您无法拨打ClassCurrent.GetAllRootNodes,因为GetAllRootNodes不是静态方法,因此您需要ClassCurrent的实例来调用它。

代表可以留在有权访问ClassCurrentMyClassLegacy的任何班级。

例如,您可以创建类似:

的smth
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);
    }
}

委托可以包装static和instance方法。唯一的区别是,对于使用实例方法创建委托,您需要拥有该方法的类的实例。

答案 2 :(得分:1)

ClassCurrentMyClassLegacy同时实现接口INodeFetcher

public interface INodeFetcher<T> { 
    List<T> GetNodes(int k);
} 

ClassCurrent从界面的实现中调用GetAllRootNodes方法,并使用MyLegacyClass方法调用GetAllLeaveNodes方法。

答案 3 :(得分:0)

为什么你想要代表呢?听起来太复杂了。我只想在一个新类中创建一个方法,当你需要调用方法时,你可以实现该方法。可以给这个类提供一些上下文信息以帮助它决定。然后我将在新方法中实现逻辑,该逻辑将决定是调用当前方法还是遗留方法。

这样的事情:

public class CurrentOrLegacySelector<T>
{

  public CurrentOrLegacySelector(some type that describe context)
  {   
     // .. do something with the context. 
     // The context could be a boolean or something more fancy.
  }

  public List<T> GetNodes(int argument) 
  {
    // Return the result of either current or
    // legacy method based on context information
  }
}

这将为您提供一个易于阅读和理解的方法的干净包装。

答案 4 :(得分:0)

作为Rune Grimstad建议主题的变体,我认为您可以使用策略模式(例如
Introduction to the GOF Strategy Pattern in C#
)。

如果您无法更改LegacyClass(因此可能无法轻松使用Cornelius建议的“接口方法”)并且您正在使用依赖注入(DI; Dependency injection),这将特别有趣。 DI(也许)会让你在正确的位置注入正确的实施(具体的策略)。

策略:

public interface INodeFetcher<T> { 
    List<T> GetNodes(int k);
}

具体策略:

public class CurrentSelector<T> : INodeFetcher<T>
{
    public List<T> GetNodes(int argument) 
    {
    // Return the result "current" method
    }
}

public class LegacySelector<T> : INodeFetcher<T>
{
    public List<T> GetNodes(int argument) 
    {
    // Return the result "legacy" method
    }
}

- &GT;注入/实例化正确的具体策略。

此致