【问题标题】:How can you inherit from a sealed class using reflection in .Net?如何使用 .Net 中的反射从密封类继承?
【发布时间】:2008-09-30 21:03:57
【问题描述】:

在你开始向我开火之前,我不想这样做,但another post 中的某个人说这是可能的。这怎么可能?我从来没有听说过使用反射继承任何东西。但是我看到了一些奇怪的东西......

【问题讨论】:

  • 嗯,我认为这就是声望派上用场的时候了。回答这个问题的人有 19 个代表(截至 9 月 30 日)他可能不知道他在说什么。另一方面,我们有你,说这是不可能的。所以,在这种情况下,你应该相信自己。
  • :) Kilhofer(2,459) 与 dalle.myopenid.com(19)

标签: .net inheritance


【解决方案1】:

如果没有要覆盖的虚函数,继承密封类没有多大意义。

如果您尝试编写一个包含虚函数的密封类,则会收到以下编译器错误:

// error CS0549: 'Seal.GetName()' is a new virtual member in sealed class 'Seal'

但是,您可以通过在基类中声明虚函数(像这样),将虚函数放入密封类中,

public abstract class Animal
{
    private readonly string m_name;

    public virtual string GetName() { return m_name; }

    public Animal( string name )
    { m_name = name; }
}

public sealed class Seal : Animal
{
    public Seal( string name ) : base(name) {}
}

但问题仍然存在,我看不出你怎么能偷偷溜过编译器让你声明一个子类。我尝试使用 IronRuby(ruby 是所有黑客语言中最黑客的),但即使它也不会让我这样做。

“密封”部分嵌入到 MSIL 中,所以我猜 CLR 本身实际上是强制执行的。您必须加载代码,反汇编它,移除“密封”位,然后重新组装它,然后加载新版本。

【讨论】:

  • 我见过一个例子,有人修补了 clr 本身。在 Dinis Cruz 和 OWASP 上进行搜索,您应该会找到它
  • 攻击者比安全系统更强大的安全漏洞不是漏洞。 “我闯进了你的大楼,把所有的锁都换成一模一样的,除了它们响应我手机的蓝牙命令。”与你拆我的锁相比,蓝牙攻击无关紧要!
  • 阿门,雅各布。让我想起了我读过的一篇文章,该文章声称 Windows 存在安全漏洞,如果您从自己的设备启动计算机,您可能会进入 Windows 硬盘驱动器上其他用户的文件夹。作者对微软的回应并不满意:“当你允许用户不受限制地物理访问计算机时,你就不能指望安全。”
  • 从密封类继承可能很有用,即使唯一能做的就是添加接口。例如,如果某人正在定义一个IDeepClone 接口,则允许将对象存储在其中可能会很有用,这些对象的类型是从String 派生的,但包含一个实现的IDeepClone,它会返回自己。如果嵌套数据结构中的一半项目是字符串,则封装将使该数据结构所需的不同对象实例的数量增加 50%(因为每个字符串都需要关联的 IDeepClonable 包装器)。
  • @alexmac 这是你指的例子吗? owasp.org/index.php/Rooting_The_CLR
【解决方案2】:

我很抱歉在另一个帖子中发布了不正确的假设,我没有正确回忆。使用以下示例,使用 Reflection.Emit,展示了如何从另一个类派生,但它在运行时失败并抛出 TypeLoadException。

sealed class Sealed
{
   public int x;
   public int y;
}

class Program
{
   static void Main(string[] args)
   {
      AppDomain ad = Thread.GetDomain();
      AssemblyName an = new AssemblyName();
      an.Name = "MyAssembly";
      AssemblyBuilder ab = ad.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
      ModuleBuilder mb = ab.DefineDynamicModule("MyModule");
      TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Class, typeof(Sealed));

      // Following throws TypeLoadException: Could not load type 'MyType' from
      // assembly 'MyAssembly' because the parent type is sealed.
      Type t = tb.CreateType();
   }
}

【讨论】:

  • 如果不能加载自己生成的东西,我认为这不算创建派生类……毕竟用@可以创建各种乱七八糟的东西987654322@,但这并不能让这些东西现在以某种方式有效。
  • @OrionEdwards:这就是我在这个答案中写的。它不会起作用,而且我之前的假设是不正确的。
  • +100,您为我节省了数小时的研究时间。这表明即使我们动态构建子类,我们也不能从密封类继承。我验证了在父类上注释掉 sealed 关键字可以防止异常。对于阅读本文的任何人,您至少需要在顶部添加 using System.Reflection.Emit;using System.Threading;。我仍在寻找一种在运行时修改非虚拟密封类方法的方法,但看起来甚至 Harmony 都使用了unsafe 关键字。我无法让 Pose 在 Unity 中工作,Cecil 只修改磁盘上的已编译程序集 :-(
【解决方案3】:

可能(如果可以的话会增加尺寸)。根据 freenode 上的人的说法,这将涉及修改字节码,使用 Reflection.Emit,并为 JIT 提供一组新的字节码。

不是我知道如何......这正是他们的想法。

【讨论】:

  • 这是唯一有意义的方法。对于所涉及的工作,怎么会有人证明这一点?简直太疯狂了。
  • 你无法合理地证明这一点。
  • 如果你这样做了,那么你实际上就不会从一个密封的类中派生出来......你会在类中搞砸以移除密封,然后进行正常的派生。这可能是解决您问题的有效方法,但我只是说,它不再源自密封类:-)
【解决方案4】:

另一位发帖人可能更多地考虑了 Reflection.Emit,而不是更常见的只读反射 API。

但是,仍然不可能(至少根据this article)。但是,在您尝试实际执行发出的代码之前,肯定有可能用 Reflection.Emit 搞砸一些不会被捕获的东西。

【讨论】:

    【解决方案5】:

    创建一个名为 GenericKeyValueBase 的新类

    把这个放进去

      public class GenericKeyValueBase<TKey,TValue>
        {
            public TKey Key;
            public TValue Value;
    
            public GenericKeyValueBase(TKey ItemKey, TValue ItemValue)
            {
                Key = ItemKey;
                Value = ItemValue;
            }
        }
    

    如果你觉得很酷,你可以从它继承,加上你可以添加额外的 Add/Remove(AddAt 和 RemoveAt)扩展方法到你的新派生类(并使其成为一个集合/字典)。

    一个简单的实现示例,您可以使用普通 System.Collections.Generic.KeyValuePair 作为基础,但可以使用上面的代码

      class GenericCookieItem<TCookieKey, TCookieValue> : GenericKeyValueBase<TCookieKey,TCookieValue>
        {
            public GenericCookieItem(TCookieKey KeyValue, TCookieValue ItemValue) : base(KeyValue, ItemValue)
            {
            }
        }
    

    【讨论】:

    • 我看不出这与从密封类继承有什么关系。
    • 实现上述内容的密封类现在可以使用 TCookieKey/Value 对象来增强伪可扩展性。该方法甚至可以隐藏在基类中...
    猜你喜欢
    • 2015-08-31
    • 1970-01-01
    • 2016-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    • 2018-02-04
    • 2021-03-15
    相关资源
    最近更新 更多