2012-03-13 16 views
18

Sin heredar pero solo con reflexión ¿es posible cambiar dinámicamente el código de un método en C#?¿Hay alguna manera de "anular" un método con la reflexión?

algo como:

nameSpaceA.Foo.method1 = aDelegate; 

No puedo cambiar/editar La clase Foo.

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

Mi objetivo final es cambiar el código de dynamicaly:

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

En System.Xml.Xsl.Runtime.XslConvert.cs

a su vez:

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

en:

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

No, C# no se puede parchear, si esa es la pregunta ... –

+1

@MarcGravell emisión permite eso. Además, volver a mezclar puede hacerlo también. ¡C# puede ser totalmente parchado! –

+1

@Baboon respondió en su respuesta –

Respuesta

12

La primera parte de esta respuesta es incorrecta, sólo estoy dejándolo de manera que la evolución en los comentarios tiene sentido. Por favor vea los EDIT (s).

usted no está buscando para la reflexión, pero emisión (que es a la inversa).

En particular, hay un método que hace exactamente lo que quiere, qué suerte!

Ver TypeBuilder.DefineMethodOverride

EDIT:
Escribir esta respuesta, acabo de recordar que re-mix le permite hacer esto también. Aunque es mucho más difícil.

Re-Mix es un marco que "simula" mixins en C#. En su aspecto básico, puede pensarlo como interfaces con implementaciones predeterminadas. Si vas más allá, se convierte en mucho más que eso.

EDIT 2: Aquí hay un ejemplo de uso para volver a mezclar (implementando INotifyPropertyChanged en una clase que no lo admite, y no tiene idea de mixins).

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

Y la prueba:

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 

Tenga en cuenta que:

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

y

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

y

[ImplementsINPC] //commented out in my example 

Tienen exactamente el mismo efecto. Es una cuestión de dónde desea definir que una mezcla particular se aplica a una clase particular.

Ejemplo 2: Igual primordial y 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<>)) 
     { 

     } 
    } 

Ese ejemplo es mi aplicación de las práctica de laboratorio se administran junto con la re-mezcla. Puede encontrar más información allí.

+0

No puede usar DefineMethodOverride para cambiar un método privado, no virtual en una clase interna. Si puedes, me encantaría verlo. Igualmente, necesitarás también cambiar la construcción para crear el subtipo, que asume que tienes control sobre lo que * crea * 'Foo' (que no es necesariamente el caso) –

+0

@MarcGravell No lo he probado personalmente, pero me inclinaría a pensar que anular los métodos privados no tiene sentido. Sin embargo, dado el escenario de ejemplo que dio después de su edición, me parece que tiene acceso al método que desea anular. Entonces, no es privado. De cualquier manera, definitivamente vale la pena echarle un vistazo a la mezcla. –

+0

hay una diferencia entre "Puedo ver lo que hace el método en el reflector" y "es un método público virtual al que tengo acceso" - Realmente no creo que la emisión vaya a ayudar aquí (y lo digo como alguien quién usa emitir regularmente) –

Cuestiones relacionadas