Использование дочернего посетителя в C # - PullRequest
0 голосов
/ 12 марта 2011

Я устанавливаю компонент тестирования и стараюсь сделать его универсальным. Я хочу использовать общий класс 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 методы для классов, не связанных с компонентом тестирования. Я пытаюсь достичь невозможного?

Ответы [ 3 ]

1 голос
/ 12 марта 2011

Поскольку ваш метод 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 #: передача нулевого значения перегруженному методу - какой метод вызывается.

1 голос
/ 12 марта 2011

Вместо определения всего интерфейса и связанных реализаций для Visiting, определите метод Visit, который принимает действие (или предикат, или Func, в зависимости от того, что вы хотите, чтобы посетитель делал).

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

ХотяЯ не совсем понимаю, что вы пытаетесь сделать, определение метода, который принимает функцию, может устранить необходимость определения всех этих интерфейсов.

0 голосов
/ 12 марта 2011

Как говорится в комментарии, accept не вызывает никаких методов посещения, поэтому я отвечаю исходя из того, что, по-моему, вы имели в виду ... Я бы подумал, что это основано на объявленном типе объекта, переданного в качестве параметра?

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

вызов обоих параметров должен привести к visit(USB_Read_Test urt) для obj1 и visit(Interface_Read_Test_Case rtc) для obj2.

...