我正在寻找一种在C#中实现访问者的紧凑方式。 该代码将在Unity3D的“对象层次结构walker”函数中使用。
主要问题是我不知道如何将“通用可调参数”声明为C#中的方法参数。
static void visitorTest(var visitor){ // <<---- which type?
int i = 0;
visitor(i);
}
可以在C ++模板函数中轻松表达
template<class Visitor> void visitorTest(Visitor visitor){
visitor(i);
}
理想情况下vistior
应该接受类,方法(或静态方法)和某种“lambda”表达式。接受“类”是可选的。
我尝试使用来自here和here的信息在C#中编写它,但我没有把它弄好。
我缺少一些基本知识,主要与委托,动作,方法和Func之间的转换相关,如果有人指出我还不知道的东西或者只是向我举了示例,我会自己弄明白(修复两个编译错误比解释所有内容的时间更短)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleTest
{
class Program
{
public delegate void Visitor(int i);
public void visitorTest(Visitor visitor){
int[] tmp = new int[10];
for (int i = 0; i < tmp.Length; i++){
tmp[i] = i;
}
foreach(var i in tmp){
visitor(i);
}
}
public static void funcCallback(int arg) {
System.Console.WriteLine("func: " + arg.ToString());
}
static void Main(string[] args)
{
//An object reference is required for the non-static field, method, or property 'ConsoleTest.Program.visitorTest(ConsoleTest.Program.Visitor)
visitorTest(new Visitor(funcCallback));
int mul = 2;
Action< int> lambda = (i) => System.Console.WriteLine("lambda: " + (2*i).ToString());
//The best overloaded method match for 'ConsoleTest.Program.visitorTest(ConsoleTest.Program.Visitor)' has some invalid arguments
//Argument 1: cannot convert from 'System.Action<int>' to 'ConsoleTest.Program.Visitor'
visitorTest(lambda);
}
}
C ++代码示例:
理想情况下,我希望拥有相应的代码片段(C ++):
#include <vector>
#include <iostream>
template<class Visitor> void visitorTest(Visitor visitor){
//initialization, irrelevant:
std::vector<int> tmp(10);
int i = 0;
for(auto& val: tmp){
val =i;
i++;
}
//processing:
for(auto& val: tmp)
visitor(val);
}
//function visitor
void funcVisitor(int val){
std::cout << "func: " << val << std::endl;
}
//class visitor
class ClassVisitor{
public:
void operator()(int arg){
std::cout << "class: " << arg*val << std::endl;
}
ClassVisitor(int v)
:val{v}{
}
protected:
int val;
};
int main(){
visitorTest(funcVisitor);
visitorTest(ClassVisitor(2));
int arg = 3;
/*
* lambda visitor: equivalent to
*
* void fun(int x){
* }
*/
visitorTest([=](int x){ std::cout << "lambda: " << arg*x << std::endl;});
}
输出:
func: 0
func: 1
func: 2
func: 3
func: 4
func: 5
func: 6
func: 7
func: 8
func: 9
class: 0
class: 2
class: 4
class: 6
class: 8
class: 10
class: 12
class: 14
class: 16
class: 18
lambda: 0
lambda: 3
lambda: 6
lambda: 9
lambda: 12
lambda: 15
lambda: 18
lambda: 21
lambda: 24
lambda: 27
visitorTest
是一个通用(模板)函数,可以将lambda表达式,类或函数作为回调。
funcTest
是函数回调。
classTest
是一个类回调。
并且main()中的最后一行有lambda回调。
我可以通过提供各种抽象基础来轻松地进行基于类的回调,但我希望有更灵活的方法,因为编写完整的类通常过于冗长,并且为这样简单的东西编写抽象基础是过度的。< / p>
在线信息表明,这样做的方法是使用Linq和Delegates,但是我在它们之间进行转换或者只是传递代表时遇到了麻烦。
么?
答案 0 :(得分:1)
删除您的Visitor委托,并将输入参数指定为Action,它可以是采用一个int参数的任何方法。
using System;
namespace Testing
{
internal class Program
{
public static void visitorTest(Action<int> visitor)
{
int[] tmp = new int[10];
for (int i = 0; i < tmp.Length; i++)
{
tmp[i] = i;
}
foreach (var i in tmp)
{
visitor(i);
}
}
public static void funcCallback(int arg)
{
System.Console.WriteLine("func: " + arg.ToString());
}
private static void Main(string[] args)
{
//An object reference is required for the non-static field, method, or property 'ConsoleTest.Program.visitorTest(ConsoleTest.Program.Visitor)
visitorTest(funcCallback);
int mul = 2;
Action<int> lambda = (i) => System.Console.WriteLine("lambda: " + (2 * i).ToString());
//The best overloaded method match for 'ConsoleTest.Program.visitorTest(ConsoleTest.Program.Visitor)' has some invalid arguments
//Argument 1: cannot convert from 'System.Action<int>' to 'ConsoleTest.Program.Visitor'
visitorTest(lambda);
Console.Read();
}
}
}
输出如下:
func: 0
func: 1
func: 2
func: 3
func: 4
func: 5
func: 6
func: 7
func: 8
func: 9
lambda: 0
lambda: 2
lambda: 4
lambda: 6
lambda: 8
lambda: 10
lambda: 12
lambda: 14
lambda: 16
lambda: 18
答案 1 :(得分:0)
经过一番乱搞之后,我想出了如何正确使用代表。更新后的示例如下。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleTest
{
class Program
{
public delegate void Visitor(int i);
public static void visitorTest(Visitor visitor){
int[] tmp = new int[10];
for (int i = 0; i < tmp.Length; i++){
tmp[i] = i;
}
foreach(var i in tmp){
visitor(i);
}
}
public static void funcCallback(int arg) {
System.Console.WriteLine("func: " + arg.ToString());
}
static void Main(string[] args)
{
visitorTest(funcCallback);
int mul = 2;
visitorTest((int i)=>{System.Console.WriteLine("lambda: " + (mul*i).ToString());});
Action<int> lambda = (int i) => { System.Console.WriteLine("lambda: " + (3 * i).ToString()); };
visitorTest(lambda.Invoke);
}
}
}
我也放弃了将类实例作为回调传递,因为无论如何我都可以通过lambdas做到这一点。
解释(如果有人后来偶然发现):
public delegate void Visitor(int i);
这一行声明名为Visitor的“delegate” type ,它基本上等同于C ++函数指针。声明后它可以用作参数:
public static void visitorTest(Visitor visitor){
使用普通函数调用语法调用。
visitor(i);
通常可以将方法和lambda表达式分配给此类型。
visitorTest(funcCallback);
visitorTest((int i)=>{System.Console.WriteLine("lambda: " + (mul*i).ToString());});
另外,this SO thread discussess differences between Action<>/Func<> and delegates。