【问题标题】:C#: delegates, compact visitor, "universal callable" argument typeC#:委托、紧凑型访问者、“通用可调用”参数类型
【发布时间】:2015-04-02 10:18:24
【问题描述】:

我正在寻找一种在 C# 中实现访问者的紧凑方法。 该代码将用于 Unity3D 中的“object hierarchy 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”表达式。接受“类”是可选的。

我确实尝试使用来自 herehere 的信息用 C# 编写它,但我没有做对。

我缺少一些基本知识,主要与委托、Action、方法和 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 回调。


我可以通过提供各种抽象基础来轻松地创建基于类的回调,但我希望有更灵活的方法,因为编写完整的类通常过于冗长,而为这种简单的事情编写抽象基础是多余的。

在线信息表明这样做的方法是使用 Linq 和 Delegates,但我无法在它们之间转换或只是传递委托。

建议?

【问题讨论】:

    标签: c# c++ linq lambda delegates


    【解决方案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
    

    【讨论】:

    • 哦。伟大的。没有想到这一点。谢谢。
    【解决方案2】:

    经过一番折腾,我弄清楚了如何正确使用委托。更新的示例如下。

    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”类型,本质上有点等价于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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-19
      • 2014-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多