在 C# 中使用子访问者

发布于 2024-10-22 03:12:55 字数 1328 浏览 1 评论 0原文

我正在设置一个测试组件并尝试保持其通用性。我想使用通用的 Visitor 类,但不确定是否使用后代类。

示例

public interface Interface_Test_Case
{
  void execute();
  void accept(Interface_Test_Visitor v);
}


public interface Interface_Test_Visitor
{
  void visit(Interface_Test_Case tc);
}


public interface Interface_Read_Test_Case
  : Interface_Test_Case
{
  uint read_value();
}


public class USB_Read_Test
  : Interface_Read_Test_Case
{
  void execute()
  { Console.WriteLine("Executing USB Read Test Case."); }

  void accept(Interface_Test_Visitor v)
  { Console.WriteLine("Accepting visitor."); }

  uint read_value()
  {
    Console.WriteLine("Reading value from USB");
    return 0;
  }
}


public class USB_Read_Visitor
  : Interface_Test_Visitor
{
  void visit(Interface_Test_Case tc)
  { Console.WriteLine("Not supported Test Case."); }

  void visit(Interface_Read_Test_Case rtc)
  { Console.WriteLine("Not supported Read Test Case."); }

  void visit(USB_Read_Test urt)
  { Console.WriteLine("Yay, visiting USB Read Test case."); }
}

// Code fragment
  USB_Read_Test test_case;
  USB_Read_Visitor visitor;
  test_case.accept(visitor);

C# 编译器使用什么规则来确定代码片段将执行 USB_Read_Visitor 中的哪些方法?

我正在尝试排除我的测试组件的依赖关系。不幸的是,我当前的 Visitor 类包含与测试组件无关的类的 visit 方法。我是否正在努力实现不可能的目标?

I am setting up a testing component and trying to keep it generic. I want to use a generic Visitor class, but not sure about using descendant classes.

Example:

public interface Interface_Test_Case
{
  void execute();
  void accept(Interface_Test_Visitor v);
}


public interface Interface_Test_Visitor
{
  void visit(Interface_Test_Case tc);
}


public interface Interface_Read_Test_Case
  : Interface_Test_Case
{
  uint read_value();
}


public class USB_Read_Test
  : Interface_Read_Test_Case
{
  void execute()
  { Console.WriteLine("Executing USB Read Test Case."); }

  void accept(Interface_Test_Visitor v)
  { Console.WriteLine("Accepting visitor."); }

  uint read_value()
  {
    Console.WriteLine("Reading value from USB");
    return 0;
  }
}


public class USB_Read_Visitor
  : Interface_Test_Visitor
{
  void visit(Interface_Test_Case tc)
  { Console.WriteLine("Not supported Test Case."); }

  void visit(Interface_Read_Test_Case rtc)
  { Console.WriteLine("Not supported Read Test Case."); }

  void visit(USB_Read_Test urt)
  { Console.WriteLine("Yay, visiting USB Read Test case."); }
}

// Code fragment
  USB_Read_Test test_case;
  USB_Read_Visitor visitor;
  test_case.accept(visitor);

What are the rules the C# compiler uses to determine which of the methods in USB_Read_Visitor will be executed by the code fragment?

I'm trying to factor out dependencies of my testing component. Unfortunately, my current Visitor class contains visit methods for classes not related to the testing component. Am I trying to achieve the impossible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

吾性傲以野 2024-10-29 03:12:55

由于您的 accept() 方法实际上并未调用任何 visit() 方法,因此没有。 :)

但是,如果您将其调用为:

void accept(Interface_Test_Visitor v)
{
    Console.WriteLine("Accepting visitor.");
    v.Visit(this); // lets invoke it this time
}

编译器将看到 this 首先作为 USB_Read_Test 的实例,然后是 Interface_Read_Test_Case,然后是 Interface_Test_Case。它将首先选择最直接的重载(或者可以使用隐式转换的重载),然后沿着继承链向下查找,直到找到适合重载的合适类型。因此在这种情况下,它将调用 visit(USB_Read_Test)。您可以通过强制转换来覆盖此行为:

v.Visit((Interface_Read_Test_Case)this); // argument is an instance of Interface_Read_Test_Case
v.Visit((Interface_Test_Case)this);      // argument is an instance of Interface_Test_Case

但是,如果您的类实现了多个接口,其中每个接口都有一个重载,但该类没有重载,您将收到必须解决的歧义错误。

例如,

interface IX { }
interface IY { }
class Class : IX, IY { }

void Call(IX _) { }
void Call(IY _) { }
// no Call(Class _) method

var c = new Class();
Call(c); // error: ambiguous call
Call((IX)c); // not ambiguous

有关详细信息,请参阅方法解析顺序和稍微相关的C#:将 null 传递给重载方法 - 调用哪个方法?

Since your accept() method doesn't actually call any of the visit() methods, none. :)

However if you invoked it as:

void accept(Interface_Test_Visitor v)
{
    Console.WriteLine("Accepting visitor.");
    v.Visit(this); // lets invoke it this time
}

the compiler will see that this is first as an instance of USB_Read_Test, then Interface_Read_Test_Case, then Interface_Test_Case. It will choose the most direct overload first (or one where implicit conversions can be used) then go down the inheritance chain until it finds a suitable type that fits an overload. So in this case, it will call visit(USB_Read_Test). You can override this behavior by casting:

v.Visit((Interface_Read_Test_Case)this); // argument is an instance of Interface_Read_Test_Case
v.Visit((Interface_Test_Case)this);      // argument is an instance of Interface_Test_Case

If however your class implements multiple interfaces, where each interface has an overload but there is none for the class, you will get ambiguity errors which you will have to resolve.

e.g.,

interface IX { }
interface IY { }
class Class : IX, IY { }

void Call(IX _) { }
void Call(IY _) { }
// no Call(Class _) method

var c = new Class();
Call(c); // error: ambiguous call
Call((IX)c); // not ambiguous

For more information, see Method resolution order and slightly related C#: Passing null to overloaded method - which method is called?.

空心↖ 2024-10-29 03:12:55

不要为访问定义整个接口和关联的实现,而是定义一个接受 Action(或 Predicate,或 Func,具体取决于您希望访问者执行的操作)的 Visit 方法。

class TestCase
{
    public void Visit(Action<T> action, T val) 
    {
        action(val);
    }

}

var tc = new TestCase();
uint some_val = 3;
tc.Visit((Action) (val) => Console.WriteLine("Val " + val));

尽管我并不完全清楚您要做什么,但定义一个采用 Function 的方法可以消除定义所有这些接口的必要性。

Instead of defining an entire interface and associated implementations for Visiting, define a Visit method that accepts an Action (or Predicate, or Func depending on what you want the Visitor to do).

class TestCase
{
    public void Visit(Action<T> action, T val) 
    {
        action(val);
    }

}

var tc = new TestCase();
uint some_val = 3;
tc.Visit((Action) (val) => Console.WriteLine("Val " + val));

Though I'm not entirely clear on what you're trying to do, defining a method that takes a Function can remove the necessity of defining all those interfaces.

一百个冬季 2024-10-29 03:12:55

正如评论所说,接受不会调用任何访问方法,所以我根据我认为你的意思回答......
我本以为它是基于作为参数传入的对象的声明类型?

USB_Read_Test obj1 = new USB_Read_Test();
Interface_Read_Test_Case obj2 = new USB_Read_Test();

使用这两个参数进行调用应导致 obj1 的 visit(USB_Read_Test urt) 和 obj2 的 visit(Interface_Read_Test_Case rtc)

As the comment says, accept doesnt invoke any visit methods, so I answer based on what i think you meant...
I would have thought it based on the declared type of the object passed in as a parameter?

USB_Read_Test obj1 = new USB_Read_Test();
Interface_Read_Test_Case obj2 = new USB_Read_Test();

invoking with both as parameters should result in visit(USB_Read_Test urt) for obj1, and visit(Interface_Read_Test_Case rtc) for obj2.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文