【问题标题】:Is there a way to "override" a method with reflection?有没有办法用反射“覆盖”一个方法?
【发布时间】:2012-03-29 21:35:08
【问题描述】:

不使用继承而只使用反射是否可以在C#中动态更改方法的代码?

类似:

nameSpaceA.Foo.method1 = aDelegate;

我无法更改/编辑 Foo 类。

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

我的最终目标是动态更改以下代码:

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

在 System.Xml.Xsl.Runtime.XslConvert.cs 中

转身:

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

进入:

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

【问题讨论】:

  • 不,C# 不能进行猴子补丁,如果这是问题...
  • @MarcGravell 发射可以实现这一点。此外,重新混合也可以做到这一点。 C# 完全可以打猴子补丁!
  • @Baboon 回复了你的回答

标签: c# reflection .net-reflector


【解决方案1】:

这个答案的第一部分是错误的,我只是留下它,以便 cmets 的演变有意义。请参阅编辑。

您不是在寻找反射,而是在寻找发射(反之亦然)。

特别是,有一种方法可以满足您的需求,真幸运!

TypeBuilder.DefineMethodOverride

编辑:
写这个答案,我只记得re-mix 也允许你这样做。不过这要困难得多。

Re-mix 是一个在 C# 中“模拟”mixin 的框架。在其基本方面,您可以将其视为具有默认实现的接口。如果你走得更远,它会变得更多。

编辑 2:这是一个使用 re-mix 的示例(在不支持它的类上实现 INotifyPropertyChanged,并且不知道 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;
    }
}

还有测试:

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

请注意:

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

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

[ImplementsINPC] //commented out in my example

具有完全相同的效果。问题在于您希望在哪里定义特定的 mixin 应用于特定的类。

示例 2:覆盖 Equals 和 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<>))
        {

        }
    }

这个例子是我对 re-mix 给出的动手实验的实现。您可以在那里找到更多信息。

【讨论】:

  • 您不能使用 DefineMethodOverride 更改内部类上的私有非虚拟方法。如果可以的话,我很想看看。同样,您还需要更改构造以创建子类型,假设您可以控制任何 创建 Foo(不一定是这种情况)
  • @MarcGravell 我没有亲自尝试过,但我倾向于认为覆盖私有方法没有意义。但是,鉴于他在编辑后给出的示例场景,在我看来他可以访问他想要覆盖的方法。所以,这不是私人的。不管怎样,re-mix 绝对值得一看!
  • “我可以看到该方法在反射器中的作用”和“它是我可以访问的公共虚拟方法”之间存在差异 - 我真的不认为 emit 在这里会有所帮助(我说作为经常使用 emit 的人)
  • 是的,他想要覆盖的方法可能不是虚拟的,或者一开始就不会有问题。排放不会那么容易帮助。然而,他可以通过反射“读取”并通过发射“写入”来重建整个类型。不过,根据班级的规模,可能需要做很多工作。
  • 在我的情况下,这是我需要发出的静态方法,我应该停止工作,尝试使用 DefineMethodOverride 还是尝试重新混合?
猜你喜欢
  • 1970-01-01
  • 2020-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-24
  • 1970-01-01
  • 1970-01-01
  • 2018-10-05
相关资源
最近更新 更多