2012-03-13 18 views
18

Senza ereditari ma solo con la riflessione è possibile modificare dinamicamente il codice di un metodo in C#?C'è un modo per "sovrascrivere" un metodo con la riflessione?

qualcosa di simile:

nameSpaceA.Foo.method1 = aDelegate; 

non posso cambiare/modificare i Foo classe.

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

Il mio obiettivo finale è quello di modificare il codice di dynamicaly:

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

In System.Xml.Xsl.Runtime.XslConvert.cs

per attivare:

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

in:

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

No, C# non può essere modificato con scimmia, se questa è la domanda ... –

+1

l'emissione @MarcGravell lo abilita. Inoltre, re-mix può farlo anche. C# può essere totalmente rattoppato! –

+1

@Baboon ha risposto sulla risposta –

risposta

12

La prima parte di questa risposta è errata, la sto solo lasciando in modo che l'evoluzione nei commenti abbia senso. Si prega di consultare il/i EDIT (s).

Non stai cercando il riflesso, ma l'emissione (che è il contrario).

In particolare, c'è un metodo che fa proprio quello che vuoi, fortunato!

Vedi TypeBuilder.DefineMethodOverride

EDIT:
Scrivendo questa risposta, ho appena ricordato che re-mix permette di fare anche questo. È molto più difficile però.

Re-mix è un framework che "simula" i mix in C#. Nel suo aspetto di base, puoi considerarlo come un'interfaccia con le implementazioni predefinite. Se vai oltre, diventa molto di più.

EDIT 2: Ecco un esempio di utilizzo per re-mix (implementazione di INotifyPropertyChanged su una classe che non lo supporta e non ha idea di mixin).

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; 
    } 
} 

E il 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 

Si prega di notare che:

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

e

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

e

[ImplementsINPC] //commented out in my example 

Hanno lo stesso effetto. È una questione di dove si desidera definire che un particolare mixin è applicato a una particolare classe.

Esempio 2: prioritario Equals e 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<>)) 
     { 

     } 
    } 

Questo esempio è il mio attuazione dei hands-on lab date con re-mix. Puoi trovare maggiori informazioni lì.

+0

Non è possibile utilizzare DefineMethodOverride per modificare un metodo privato non virtuale su una classe interna. Se puoi, mi piacerebbe vederlo. Allo stesso modo, dovresti anche cambiare la costruzione per creare il sottotipo, il che presuppone che tu abbia il controllo su qualunque cosa * crei * 'Foo' (che non è necessariamente il caso) –

+0

@MarcGravell Non l'ho provato personalmente, ma sarei incline a pensare che l'ignoranza dei metodi privati ​​non abbia senso. Comunque, dato lo scenario di esempio che ha dato dopo la sua modifica, mi sembra che abbia accesso al metodo che vuole scavalcare. Quindi, non è privato. In ogni caso, re-mix vale sicuramente la pena dare un'occhiata! –

+0

c'è una differenza tra "Riesco a vedere cosa fa il metodo in reflector" e "È un metodo pubblico virtuale a cui ho accesso" - Non credo che emetterà davvero aiuto qui (e lo dico come qualcuno chi usa emette regolarmente) –

Problemi correlati