Недавно я экспериментировал с реализацией шаблона посетителя, где я пытался применять методы Accept & Visit с помощью общих интерфейсов:
public interface IVisitable<out TVisitable> where TVisitable : IVisitable<TVisitable>
{
TResult Accept<TResult>(IVisitor<TResult, TVisitable> visitor);
}
Цель -whose состоит в том, чтобы 1) отметить определенный тип "Foo" как посетимый таким посетителем, который, в свою очередь, является "посетителем такого типа Foo" и 2) применяет метод Accept правильной подписи к типу реализации, доступному для посещения, например так:
public class Foo : IVisitable<Foo>
{
public TResult Accept<TResult>(IVisitor<TResult, Foo> visitor) => visitor.Visit(this);
}
Пока что так хорошо, интерфейс посетителя:
public interface IVisitor<out TResult, in TVisitable> where TVisitable : IVisitable<TVisitable>
{
TResult Visit(TVisitable visitable);
}
-should 1) отметьте посетителя как "способного посетить" TVisitable 2), какой тип результата (TResult) для этого TVisitable должен быть 3) принудительно использовать метод Visit для правильной подписи для каждого TVisitable. ", вот так:
public class CountVisitor : IVisitor<int, Foo>
{
public int Visit(Foo visitable) => 42;
}
public class NameVisitor : IVisitor<string, Foo>
{
public string Visit(Foo visitable) => "Chewie";
}
Довольно приятно и красиво, это позволяет мне написать:
var theFoo = new Foo();
int count = theFoo.Accept(new CountVisitor());
string name = theFoo.Accept(new NameVisitor());
Отлично.
Теперь начинаются печальные времена, когда я добавляю еще один доступный тип, например:
public class Bar : IVisitable<Bar>
{
public TResult Accept<TResult>(IVisitor<TResult, Bar> visitor) => visitor.Visit(this);
}
который можно посетить, скажем, только CountVisitor
:
public class CountVisitor : IVisitor<int, Foo>, IVisitor<int, Bar>
{
public int Visit(Foo visitable) => 42;
public int Visit(Bar visitable) => 7;
}
который внезапно прерывает вывод типа в методе Accept! (это разрушает весь дизайн)
var theFoo = new Foo();
int count = theFoo.Accept(new CountVisitor());
давая мне:
"Аргументы типа для метода
'Foo.Accept<TResult>(IVisitor<TResult, Foo>)'
не могут быть выведены из использования."
Может ли кто-нибудь объяснить, почему это так? Существует только одна версия IVisitor<T, Foo>
которую реализует CountVisitor
или, если IVisitor<T, Bar>
не может быть устранена по какой-либо причине, у обоих из них есть один и тот же T
int
, = no other тип все равно будет работать. Выдает ли вывод типа, как только есть только один подходящий кандидат? (Забавный факт: ReSharper считает int
в theFoo.Accept<int>(...)
избыточным: P, хотя он не будет компилироваться без него)