2010-03-17 87 views
6

我目前在2个抽象类中有2个具体方法。一个类包含当前方法,而另一个包含传统方法。例如。c#委托和抽象类

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

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

不过,我对这个话题的一些问题(经过一些google搜索):

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) 

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

谢谢。

回答

7

你试图使用委托(有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; 
    }; 

拉姆达的形式是(arguments) => { body; }。参数用逗号分隔。如果只有一个,可以省略括号。如果不需要参数,请输入一对空的括号:()。如果身体只有一个陈述长,你可以省略大括号。如果只是一个表达式,则可以省略大括号和return关键字。在本体中,实际上可以引用封闭范围中的任何变量和方法(除了ref/out参数外)。

几乎没有任何需要使用new来创建委托实例。很少需要声明自定义委托类型。使用Func为返回返回void代表值和Action代表。

每当你需要通过周围的东西就像一个方法的对象(接口或类是否),然后使用一个委托,而是和你将能够避免很多乱七八糟的。

特别地,避免定义与一种方法接口。它只是意味着不是能够写一个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 */ 

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

+0

如果你希望委托映射到一个特定的方法,如果它是类型'ClassCurrent'('GetAllRootNodes')和另一个方法,如果它是类型'MyClassLegacy'('GetAllLeaveNodes')那么你将不得不知道类型的情况。而如果它共享一个通用接口,则不需要知道它实现给定接口的类型。用于为每种类型设置委托的代码将包含一个if来检查类型或将特定类型作为每个允许类型的参数的方法。 – Cornelius 2010-03-17 08:46:52

+0

@Cornelius - 你认为代表是什么?这听起来像你认为它与C中的函数指针相同。 – 2010-03-17 09:10:35

+2

委托函数比函数指针更强大,因为它们是“第一类方法”并且具有闭包,但这只允许您以多态方式传递它们,而不会消除上面评论中设置它的复杂性。 – Cornelius 2010-03-17 09:29:11

1

根据某些条件,您可以拥有一个根据不同方法进行初始化的委托。

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

代表可以留在任何可以访问ClassCurrentMyClassLegacy的类中。

例如,您可以创建水木清华这样的:

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

代表们能包裹静态和实例方法。唯一的区别是,对于使用实例方法创建委托,您需要拥有该方法的类的实例。

+0

一旦委托例如创建public delegate void del(int i),代码保留在特定的类中。我在想,代表需要留在具有感兴趣方法的课程中,否则将无法找到它们。另外,我不明白为什么代表可以创建一个静态方法? – BeraCim 2010-03-17 07:35:41

+0

我已经更新了答案。 – 2010-03-17 07:47:47

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

这会给你一个干净的包装器,易于阅读和理解的方法。

+0

我认为使用委托的解决方案更加灵活 - 您将需要更少的操作来支持第三种此类方法。另一方面,第三种方法未来可能不会出现:) – 2010-03-17 07:52:27

+0

委托并不复杂,它要简单得多。 – 2010-03-17 08:19:26

1

让双方ClassCurrentMyClassLegacy实现接口INodeFetcher

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

对于ClassCurrent呼叫从接口的实现GetAllRootNodes方法和MyLegacyClassGetAllLeaveNodes方法。

+3

如果您的界面中有一个方法,请改为使用委托。你将为自己节省一大笔代码。一个委托就像一个单一方法的接口,加上大量的语言支持,这意味着每次你想实现它时,你不必编写单独的命名类。您要查找的类型是'Func >'。 – 2010-03-17 07:53:49

+0

@Daniel但你将不能够使用多态委托未声明的一个共同的基类或接口,你仍然需要建立代表也将需要的代码相当量。 – Cornelius 2010-03-17 08:03:51

+0

看到我的答案。设置委托需要比声明和实现接口少得多的代码。代理也是多态的:调用者只是获得一个值,他们可以通过调用方法调用,而不是绑定到特定的实现。这完全像一个单一方法的界面,但使用它所需的仪器代码要少得多。 – 2010-03-17 08:15:05

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

- >喷射/实例化正确的具体策略。

问候

+0

但接口有一种方法。这只是使用委托消除了大量不必要的手工编码的秘诀。 – 2010-03-17 08:18:31

+0

我在哪里错过了什么?顺便说一下,您可能已经注意到我不擅长委托编程;如果你可以请温柔:) – scherand 2010-03-17 09:32:07

+0

你看到我的答案?我不确定还有什么要补充的......如果你只关心今天的需求,那么你并不需要以任何特定的方式组织你的代码。几乎所有的高级语言功能都是关于简化程序的未来演变。所以今天你可能只有两个实现,但未来呢? – 2010-03-17 10:54:26