2012-03-13 20 views
18

Bez dziedziczenia, ale tylko z refleksji jest możliwe dynamicznie zmienić kod metody w C#?Czy istnieje sposób, aby "przesłonić" metodę z odbiciem?

coś takiego:

nameSpaceA.Foo.method1 = aDelegate; 

nie mogę zmienić/edytować Foo klasy.

namespace nameSpaceA 
{ 
    class Foo 
    { 
     private void method1() 
     { 
      // ... some Code 
     } 
    } 
} 

Moim ostatecznym celem jest zmiana dynamicznie kod:

public static IList<XPathNavigator> EnsureNodeSet(IList<XPathItem> listItems); 

W System.Xml.Xsl.Runtime.XslConvert.cs

się kolejno:

if (!item.IsNode) 
    throw new XslTransformException(Res.XPath_NodeSetExpected, string.Empty); 

do:

if (!item.IsNode) 
    throw new XslTransformException(Res.XPath_NodeSetExpected, item.value); 
+4

Nie, C# nie może być łatane małpkami, jeśli to jest pytanie ... –

+1

@MarcGravell emisja to umożliwia. Również re-mix może to zrobić. C# może być całkowicie poprawiony przez małpy! –

+1

@Baboon odpowiedział na twoją odpowiedź –

Odpowiedz

12

Pierwsza część tej odpowiedzi jest błędna, opuszczam ją tylko tak, aby ewolucja w komentarzach miała sens. Zobacz EDYCJĘ.

Nie szukasz refleksji, ale emisji (która jest odwrotnie).

W szczególności istnieje metoda, która robi dokładnie to, co chcesz, masz szczęście!

Zobacz TypeBuilder.DefineMethodOverride

EDIT:
Pisząc tę ​​odpowiedź, po prostu pamiętać, że re-mix pozwala na to zbyt. Jest to jednak trudniejsze.

Re-mix to framework, który "symuluje" mixiny w języku C#. W swoim podstawowym aspekcie możesz myśleć o nim jako o interfejsach z domyślnymi implementacjami. Jeśli pójdziesz dalej, stanie się znacznie więcej.

EDYCJA 2: Oto przykład użycia do re-mixu (implementacja INotifyPropertyChanged na klasie, która go nie obsługuje i nie ma pojęcia o mixinach).

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Remotion.Mixins; 
using System.ComponentModel; 
using MixinTest; 

[assembly: Mix(typeof(INPCTester), typeof(INotifyPropertyChangedMixin))] 

namespace MixinTest 
{ 
    //[Remotion.Mixins.CompleteInterface(typeof(INPCTester))] 
    public interface ICustomINPC : INotifyPropertyChanged 
    { 
     void RaisePropertyChanged(string prop); 
    } 

    //[Extends(typeof(INPCTester))] 
    public class INotifyPropertyChangedMixin : Mixin<object>, ICustomINPC 
    { 
     public event PropertyChangedEventHandler PropertyChanged; 

     public void RaisePropertyChanged(string prop) 
     { 
      PropertyChangedEventHandler handler = this.PropertyChanged; 
      if (handler != null) 
      { 
       handler(this, new PropertyChangedEventArgs(prop)); 
      } 
     } 
    } 

    public class ImplementsINPCAttribute : UsesAttribute 
    { 
     public ImplementsINPCAttribute() 
      : base(typeof(INotifyPropertyChangedMixin)) 
     { 

     } 
    } 

    //[ImplementsINPC] 
    public class INPCTester 
    { 
     private string m_Name; 
     public string Name 
     { 
      get { return m_Name; } 
      set 
      { 
       if (m_Name != value) 
       { 
        m_Name = value; 
        ((ICustomINPC)this).RaisePropertyChanged("Name"); 
       } 
      } 
     } 
    } 

    public class INPCTestWithoutMixin : ICustomINPC 
    { 
     private string m_Name; 
     public string Name 
     { 
      get { return m_Name; } 
      set 
      { 
       if (m_Name != value) 
       { 
        m_Name = value; 
        this.RaisePropertyChanged("Name"); 
       } 
      } 
     } 

     public void RaisePropertyChanged(string prop) 
     { 
      PropertyChangedEventHandler handler = this.PropertyChanged; 
      if (handler != null) 
      { 
       handler(this, new PropertyChangedEventArgs(prop)); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
    } 
} 

a test:

static void INPCImplementation() 
     { 
      Console.WriteLine("INPC implementation and usage"); 

      var inpc = ObjectFactory.Create<INPCTester>(ParamList.Empty); 

      Console.WriteLine("The resulting object is castable as INPC: " + (inpc is INotifyPropertyChanged)); 

      ((INotifyPropertyChanged)inpc).PropertyChanged += inpc_PropertyChanged; 

      inpc.Name = "New name!"; 
      ((INotifyPropertyChanged)inpc).PropertyChanged -= inpc_PropertyChanged; 
      Console.WriteLine(); 
     } 

static void inpc_PropertyChanged(object sender, PropertyChangedEventArgs e) 
     { 
      Console.WriteLine("Hello, world! Property's name: " + e.PropertyName); 
     } 
//OUTPUT: 
//INPC implementation and usage 
//The resulting object is castable as INPC: True 
//Hello, world! Property's name: Name 

Uwaga:

[assembly: Mix(typeof(INPCTester), typeof(INotifyPropertyChangedMixin))] 

i

[Extends(typeof(INPCTester))] //commented out in my example 

i

[ImplementsINPC] //commented out in my example 

Mieć dokładnie taki sam efekt. Chodzi o to, gdzie chcesz zdefiniować, że konkretny mixin jest stosowany do konkretnej klasy.

Przykład 2: Nadrzędnym Równa i GetHashCode

public class EquatableByValuesMixin<[BindToTargetType]T> : Mixin<T>, IEquatable<T> where T : class 
    { 
     private static readonly FieldInfo[] m_TargetFields = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 

     bool IEquatable<T>.Equals(T other) 
     { 
      if (other == null) 
       return false; 
      if (Target.GetType() != other.GetType()) 
       return false; 
      for (int i = 0; i < m_TargetFields.Length; i++) 
      { 
       object thisFieldValue = m_TargetFields[i].GetValue(Target); 
       object otherFieldValue = m_TargetFields[i].GetValue(other); 

       if (!Equals(thisFieldValue, otherFieldValue)) 
        return false; 
      } 
      return true; 
     } 

     [OverrideTarget] 
     public new bool Equals(object other) 
     { 
      return ((IEquatable<T>)this).Equals(other as T); 
     } 

     [OverrideTarget] 
     public new int GetHashCode() 
     { 
      int i = 0; 
      foreach (FieldInfo f in m_TargetFields) 
       i ^= f.GetValue(Target).GetHashCode(); 
      return i; 
     } 
    } 

    public class EquatableByValuesAttribute : UsesAttribute 
    { 
     public EquatableByValuesAttribute() 
      : base(typeof(EquatableByValuesMixin<>)) 
     { 

     } 
    } 

To przykładem jest moja realizacja hands-on lab podanych z re-mix. Możesz tam znaleźć więcej informacji.

+0

Nie możesz użyć DefineMethodOverride, aby zmienić prywatną, nie wirtualną metodę na wewnętrznej klasie. Jeśli możesz, bardzo bym chciał to zobaczyć. Podobnie, musisz także zmienić konstrukcję, aby utworzyć podtytuł, który zakłada, że ​​masz kontrolę nad czymkolwiek * tworzy * 'Foo' (co niekoniecznie musi być) –

+0

@MarcGravell Nie osobiście tego wypróbowałem, ale byłbym skłonny sądzić, że nadrzędne prywatne metody nie mają sensu. Jednakże, biorąc pod uwagę przykładowy scenariusz, który dał po swojej edycji, wydaje mi się, że ma dostęp do metody, którą chce nadpisać. Więc nie jest prywatny. Tak czy inaczej, re-mix zdecydowanie warto rzucić okiem! –

+0

Istnieje różnica między "Widzę, co metoda działa w odbiciu" i "jest to publiczna wirtualna metoda, do której mam dostęp" - naprawdę nie sądzę, że emitowanie pomoże tutaj (i mówię to jako ktoś kto używa emitować regularnie) –

Powiązane problemy