Лучший способ решить, если тип может поддерживать интерфейс? (Уток-Typing) - PullRequest
2 голосов
/ 23 ноября 2010

Я строю тип во время выполнения, используя Reflection.Emit.Конечный пользователь предоставляет базовый тип и какие интерфейсы должен поддерживать новый тип.Если в интерфейсе есть члены, которые базовый тип не может поддерживать, я создаю метод-заглушку, который вызывает делегат, хранящийся в статическом поле (я поддерживаю только неуниверсальные методы с 15 или менее параметрами, без параметров ref или out, поскольку это мой текущийтребования. Пожалуйста, не поднимайте проблемы с этим ограничением. Делегат принимает первый параметр baseType), который пользователь может предоставить, прежде чем пытаться создать тип.

Однако я бы хотел избежать созданиязаглушки делегатов для членов интерфейса, которые могут быть удовлетворены типом.например,

public class Goose
{
     public void Quack()
     {
       // quack implementation details go here.
     }
}

public interface IDuck
{
     void Quack()
}

Мне бы хотелось, чтобы, если вы отправили сюда Goose с new[]{typeof(IDuck)} моему строителю, я не буду создавать заглушку для void Quack(), поскольку гусь удовлетворяет интерфейсу.

Сопоставление интерфейса не работает, поскольку Goose не реализует IDuck, и я не могу попросить вновь созданный тип для сопоставления интерфейса, поскольку TypeBuilder не поддерживает его для типов, которые должны бытьпостроенный.

Как я могу решить это способом, который является удаленно эффективным?Я должен только исследовать общедоступные члены, и если тип явно реализует интерфейс с тем же методом, я могу предположить, что он не должен использоваться в качестве цели.(Например, если Goose реализовал void IGoose.Quack(), тогда он не должен считаться целью для void IDuck.Quack()).(В любом случае, BindingFlags.Public | BindingFlags.Instance должно быть достаточно, чтобы отфильтровать эти элементы).

Ответы [ 2 ]

0 голосов
/ 15 октября 2012

Это фактически вопрос эффективного сравнения имен и подписей MemberInfos.Вы можете получить строку, содержащую имя и подпись участника, вызвав ToString в MemberInfo.Эта строка может использоваться, чтобы определить, являются ли два члена эквивалентными сигнатурам.Если вы поместите строки в HashSet, сравнение будет весьма эффективным.

Тот факт, что ToString создает сопоставимую строку подписи, используется .NET Framework внутри для сериализации и десериализации MemberInfoобъекты, однако, AFAIK, это на самом деле не задокументировано.Поэтому, если вы не хотите полагаться на это недокументированное поведение, вы можете создать свои собственные строки подписи и сравнить их.Однако следует помнить, что это может стать довольно сложным, если принять во внимание параметры универсального типа.

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

Вы должны получить все открытые реализованные элементы базового типа через отражение в хэш-наборе (MemberInfo реализует GetHashCode. Я не знаю, сработает ли это при сравнении элементов разных типов, вам может понадобиться ваш хеш-объект для сопоставления только типов подписи name), затем, когда вы перебираете членов нового интерфейса, вы генерируете код заглушки, только если он не существует в хэш-наборе.

Не является проблемой объявить интерфейс в TypeBuilder до того, как члены будут там, пока вы не вызовете CreateType, пока они не будут там, и вам не нужно заботиться о том, какие другие интерфейсы реализует базовый тип, так как вас волнует только о подписи участников, соответствующих вашему новому интерфейсу.

Это не должно быть плохо с точки зрения производительности, если вы кешируете результирующий тип по интерфейсу и базовому типу.

...