为啥 C# 可以在运行时处理这种明显愚蠢的对象提升?

Posted

技术标签:

【中文标题】为啥 C# 可以在运行时处理这种明显愚蠢的对象提升?【英文标题】:How come C# can handle this obviously idiotic object promotion during run time?为什么 C# 可以在运行时处理这种明显愚蠢的对象提升? 【发布时间】:2017-09-02 23:28:12 【问题描述】:

我非常喜欢 C# 语言。我只是在玩,永远不会在生产代码中使用下面的代码。显然编译器被结构的布局所迷惑。但是为什么 Super 类上的字符串仍然可以在运行时读写呢?我会预料到一些内存访问冲突。在运行时检查类型,它说它是 Base 类型,请参阅NoProblem() 函数执行。没有实例化 Super 类。

它怎么能这样运作?

using System;
using System.Runtime.InteropServices;

namespace Fiddle

    class Program
    
        static void Main(string[] args)
        
            var b = new Base
            
                IntOnBase = 1
            ;
            var overlay = new Overlay();
            overlay.Base = b;
            var super = overlay.Super;
            var intValue = super.IntOnBase;
            super.StringOnSuper = "my test string";
            var stringValue = super.StringOnSuper;
            super.NoProblem();
            Expressions.Fiddle();
        
    

    [StructLayout(LayoutKind.Explicit)]
    public struct Overlay
    
        [FieldOffset(0)]
        public Super Super;
        [FieldOffset(0)]
        public Base Base;
    

    public class Super : Base
    
        public string StringOnSuper  get; set; 

        public void NoProblem()
        
            Console.WriteLine("You know, I am really a " + this.GetType().Name + " kind of class.");
        
    

    public class Base
    
        public int IntOnBase  get; set; 
    

【问题讨论】:

Super 比 Base 大,如果您尝试将 Super 转换为 Base,您只会遇到问题 super.StringOnSuper 你分配它之前的值是多少?我希望它未初始化,因为Base 的构造函数不知道它。 看起来类似于this question。有人建议使用FieldOffset 应视为unsafe 也见于this question 这不是 C# 功能,语言规范一次也没有提及。这是 CLR 公开的一项功能。它有一个非常实际的需求,它声明了一个 union,这是一种在许多其他语言中都可用的类型。但不是 C#,联合从根本上说是类型不安全的。在例如 winapi 中得到大量使用,如果没有它,他们就无法编写 .NET Framework。在纯粹之上,C# 也是一种解决实际编程问题的非常实用的语言。现实世界并不纯粹。 【参考方案1】:

好吧,你告诉 CLR 使用 StructLayout 提前布置内存(我应该提醒一下,这是基于我今天在实验和阅读其他建议的答案后的学习

您可以在这里看出 CLR 实际上并没有实例化任何东西。这将引发 NPE。您可以在 super 上使用构造函数。它没有被调用,也不会。

基本上你是在直接访问内存,因为stringint 等都是内置类型,你可以安全地与它们交互。这可能需要用户更多的“意图”,而其他评论的问题都指向这需要unsafe 声明。

class Program

    static void Main(string[] args)
    
        var b = new Base
        
            IntOnBase = 1
        ;
        var overlay = new Overlay();
        overlay.Base = b;
        var super = overlay.Super;
        var intValue = super.IntOnBase;
        super.StringOnSuper = 8;
        var stringValue = super.StringOnSuper;
        System.Diagnostics.Debug.WriteLine(stringValue);
        super.NoProblem();
    


[StructLayout(LayoutKind.Explicit)]
public struct Overlay

    [FieldOffset(0)]
    public Super Super;
    [FieldOffset(0)]
    public Base Base;

public class NewClass

    public string cat  get; set; 

public class Super : Base

    private Super imNull;
    public Super()
    
       // imNull = new Super();
        System.Diagnostics.Debug.WriteLine("well i get initialized...");
    
    public int StringOnSuper  get; set; 

    public void NoProblem()
    
        System.Diagnostics.Debug.Write("You know, I am really a " + this.GetType().Name + " kind of class. But my super is " + imNull.ToString() );
    


public class Base

    public int IntOnBase  get; set; 

【讨论】:

以上是关于为啥 C# 可以在运行时处理这种明显愚蠢的对象提升?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 C# 数组在转换为对象时会丢失类型符号信息?

为啥 Castle Windsor 拦截器会破坏 C# 动态对象上方法的运行时绑定?

c#里为啥有的使用时函数需要new一个对象而有的不用?为啥不直接调用就好?

为啥 AMD-CPU 有这么愚蠢的暂停时间

为啥开关(Java)会发生这种情况? [复制]

C#:为啥我必须在类的变量中使用公共访问修饰符?