【问题标题】:How do the In and Out attributes work in .NET?In 和 Out 属性在 .NET 中如何工作?
【发布时间】:2012-02-22 17:33:10
【问题描述】:

我一直在尝试使用以下代码跨 AppDomain 边界序列化数组:

public int Read(byte[] buffer, int offset, int count)
{
    return base.Read(buffer, offset, count);
}

作为猜测,在注意到其他地方的属性之后,我用[In][Out] 属性标记了方法的参数,这似乎导致参数的行为就像它们通过引用传递一样。

例如:

public int Read([In, Out] byte[] buffer, int offset, int count)
{
    return base.Read(buffer, offset, count);
}

在我添加属性之前,buffer 变量的内容在从方法返回后越过AppDomain 边界丢失。

类 (SslStream) 继承自 MarshalByRefObject,但未使用 Serializable 属性进行标记。这是使参数按值传递的唯一方法吗?当类被序列化时,这些属性是否被.NET以某种方式识别?并且它们是否真的导致参数通过引用传递,还是只是复制了内容?

【问题讨论】:

    标签: c# serialization parameters attributes marshalbyrefobject


    【解决方案1】:

    这是 .NET Remoting 的一个非常缺乏文档记录的功能。它与您的类是 [Serializable] 还是从 MarshalByRefObject 派生没有任何关系。这里的问题是 argument 如何跨 AppDomain 边界编组。调用本身是通过远程处理在后台进行的。数组在调用后不会自动被封送回来,这显然是一种性能优化。只有 [Out] 属性是必需的,[In] 是隐含的。我在 MSDN 中找不到任何相关文档,仅来自遇到相同问题的人的 a blog post(向下滚动到“在远程处理中使用 OutAttribute”)。

    一些代码可以玩:

    using System;
    using System.Runtime.InteropServices;
    
    class Program {
        static void Main(string[] args) {
            var ad = AppDomain.CreateDomain("second");
            var t = (Test)ad.CreateInstanceAndUnwrap(typeof(Test).Assembly.FullName, typeof(Test).FullName);
            var b = new byte[] { 1 };
            t.Read(b);
            System.Diagnostics.Debug.Assert(b[0] == 2);
        }
    }
    
    class Test : MarshalByRefObject {
        public void Read([Out]byte[] arg) {
            arg[0] *= 2;
        }
    }
    

    【讨论】:

    • 太好了,谢谢!我以前从未听说过这些属性,但是如果没有 [Out] 属性,我所面临的问题几乎无法解决。幸运的是,我在 dotPeek/ILSpy 中注意到了它们。数组也没有被编组回来似乎令人惊讶,因为现在我必须用一个具有属性方法的类来包装整个类。可惜他们不能从调用方应用......
    • 进一步检查,System 命名空间中的许多 Stream 类确实具有标有 [In] [Out] 属性的 Read() 方法的 'buffer' 参数。我想知道 System.IO 中的 NegotiateStream 和 SslStream 是否也应该具有这些属性,因为它们在 .NET 4 中不存在。
    【解决方案2】:

    请注意,自发布原始接受答案以来,已添加 additional documentation

    方向属性的定义是:

    • [In]:在非托管代码中修改的数据不会写回托管代码。默认情况下,所有按值传递的数据都被视为 [In]。
    • [In/Out]:在非托管代码中修改的数据写回托管代码。默认情况下,通过引用(包括使用 ref 关键字)传递的数据被视为 [In/Out]。
    • [Out]:由 out C# 关键字修改的参数必须按预期传回 - 并且默认为 [Out]。显式使用 [Out] 与 [In/Out] 的行为相同(始终假定为 [In])。

    所以显式修改这些的关键用途是:

    • 指示程序不要传回通过引用传递的修改数据 - 使用 [In]
    • 通过使用 [In/Out] 或简单地 [Out](它们是等效的)来指示程序返回传递给非托管代码的修改数据。

    后者对于默认按值传递的引用类型特别有用,例如值类型的数组 - 例如字节[]。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-11
      • 2020-11-29
      • 2020-12-20
      • 2021-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多