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以某种方式识别?它们真的会导致参数通过引用传递,还是只是复制了内容?

4

2 回答 2

12

这是 .NET Remoting 的一个非常缺乏文档记录的功能。它与您的类是 [Serializable] 还是从 MarshalByRefObject 派生没有任何关系。这里的问题是如何跨 AppDomain 边界编组参数。调用本身是通过远程处理在后台进行的。数组不会在调用后自动被封送回来,这显然是一种性能优化。只有 [Out] 属性是必需的,[In] 是隐含的。我在 MSDN 中找不到任何相关文档,只是遇到相同问题的人的博客文章(向下滚动到“在远程处理中使用 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;
    }
}
于 2012-01-31T03:27:49.920 回答
1

需要注意的是,自从发布了原始接受的答案以来,还添加了其他文档。

方向属性的定义是:

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

因此,显式修改这些的关键用途是:

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

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

于 2021-10-04T22:13:59.393 回答