2010-11-23 3 views
2

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

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

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 должно быть достаточно, чтобы отфильтровывать эти элементы).

+0

Вы пробовали использовать [Duck Typing Project] (http://www.deftflux.net/blog/page/Duck-Typing-Project.aspx)? – cdhowie 2010-11-23 18:27:02

ответ

0

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

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

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

0

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

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