我目前在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)
我可能误解了一些东西......但是如果我必须在调用类中手动声明一个委托,并且如上所述手动传递函数,那么我开始质疑委托是否是一种好的方法来处理我的问题。
感谢。
答案 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
的实例来调用它。
代表可以留在有权访问ClassCurrent
和MyClassLegacy
的任何班级。
例如,您可以创建类似:
的smthclass 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)
让ClassCurrent
和MyClassLegacy
同时实现接口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;注入/实例化正确的具体策略。
此致