2010-11-13 2 views
4

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

public class Base{ 
     public virtual void Method(){ 
      Console.WriteLine("Base"); 
     } 
    } 

    public class Child : Base{ 
     public override void Method(){ 
      Console.WriteLine("Child"); 
     } 
    } 

Если где-то в коде у меня есть следующие ::

var action = Delegate.CreateDelegate(typeof(Action<Base>), typeof(Base).GetMethod("Method")) as Action<Base>; 
action(new Child()); 

Вывод этой программы является Child. Мне бы очень хотелось, чтобы это было Base. Я пробовал то же самое с деревьями выражений, и получаю тот же результат, что и испускаемый IL использует метод callvirt. Это единственный способ сделать что-то подобное с помощью Reflection.Emit?

Причина, по которой я прошу, заключается в том, что я использую построитель типов, чтобы переопределить поведение класса. Если бы я сам написал метод, я мог бы просто пойти base.Method() или что-то еще, но некоторые из методов поведения могут быть определены только динамически во время выполнения, поскольку учет многих возможных случаев будет очень утомительным.

Поскольку я создаю класс, производный от Base во время выполнения, если я пытаюсь вызвать Method() внутри Method() перегрузки Я делаю это приводит к бесконечной рекурсии и переполнение стека исключений. (не так хорошо, как хотелось бы).

Это для проекта стиля AOP, где я добавляю некоторую логику методам во время выполнения. Я отмечаю методы с атрибутами, тогда у меня есть построитель типов, которые создают методBuilders, который кормит тело конструктора меток с помощью дерева выражений, используя CompileToMethod(methodbuilder)http://msdn.microsoft.com/en-us/library/dd728224.aspx, Это тонна проще, чем reflection.emit, поскольку логика нетривиальна, Я добавляю. Цель состоит в том, что у меня есть фабрика, которая выплевывает новый класс, который, когда я вызываю Method(), сначала выполняет некоторую логику, прежде чем в конечном счете вызовет базовую реализацию.

+7

Почему люди борются с системой типов? – ChaosPandion

+0

Я генерирую тип во время выполнения. Мне нужен способ вызова методов в классе, из которого я получаю, я не хочу использовать 'Reflection.Emit', если я могу ему помочь? –

+0

Опять же, что, если метод абстрактный? И если вы хотите вызвать базовый метод, почему вы их переопределяете? – jason

ответ

1

Да, Reflection.Emit - это единственный способ, предоставляемый платформой .NET для реализации перегрузок методов. Поскольку другие API-интерфейсы не используются при перегрузке, они не обеспечивают никакого способа привязки к базовой реализации.

+0

интересно, для чего я хочу сохранить дерево выражений в построителе методов, если я не могу использовать метод, созданный деревом выражений? –

+0

Ну, 'MethodBuilder', реализованный с использованием' ILGenerator', и использование деревьев выражений может вызывать друг друга (что-либо, использующее 'MethodBuilder', по-прежнему считается частью Reflection.Emit), я думаю, что это точка ответа Гейба. Поэтому используйте каждый API для своих сильных сторон. –

0

Что, возможно, произойдет здесь?

class Base { public abstract void Method(); } 
class Child { 
    public override void Method() { 
     Console.WriteLine("Child.Method"); 
    } 
} 

Action<Base> magicalAction = // defined somehow 
magicalAction(new Child()); // aiya! 

Вы пытаетесь победить точку виртуальных методов. Зачем?

+0

Я пересмотрел свой вопрос, включив в свой разум. –

+0

Было бы глупо пытаться вызвать абстрактный метод, и я, скорее всего, получу исключение, чтобы помешать мне попробовать что-то немое? –

1

Может быть, вы можете использовать такой обходной путь:

public class Base{ 
    public virtual void Method(){ 
     MethodImpl(); 
    } 
    public void MethodImpl(){ 
     Console.WriteLine("Base"); 
    } 
} 

public class Child : Base{ 
    public override void Method(){ 
     Console.WriteLine("Child"); 
    } 
} 

Теперь вы можете создать делегат, представляющий MethodImpl.

+0

это работает, но невероятно утомительно и потребует от меня написать базовый 'MethodImpl' для всех методов, которые я планирую переопределить. Я не могу позволить себе это сделать. –

+0

Вы бы хотели, чтобы 'MethodImpl' был' protected', потому что он должен быть доступен только из производных классов. – Gabe

0

С Reflection.Emit такой сложный способ построения целого метода, я бы рекомендовал использовать Reflection.Emit для создания частных методов только для вызова методов base. Затем вы можете обратиться к этим методам со своих Expression.

+0

Это на самом деле план. К сожалению, я обнаружил, что вы не можете создавать параметры 'TypeBuilder' и не можете вызывать' MethodBuilder' из деревьев выражений, что делает CompileToMethod довольно бесполезным. Думаю, вы можете сделать статический метод, который нельзя назвать без отражения? –

+0

Michael: Значит, вы говорите, что вы не можете вызвать вспомогательный метод из ваших деревьев выражений? Я думаю, вы можете обойти это, создав промежуточный класс, который наследуется от базы. Затем, когда у вас есть фактическая сборка для этого класса, вы можете получить свой фактический класс, который использует деревья выражений. – Gabe