gpt4 book ai didi

c# - 将字段作为参数传递与将方法调用作为参数传递有何不同?

转载 作者:行者123 更新时间:2023-11-30 21:26:38 24 4
gpt4 key购买 nike

我正在一个带有打印机的智能设备上编写 .Net CE 应用程序。我在 StringBuilder 对象中收集数据,然后尝试打印它。这是我打印它的方式

var receipt = new StringBuilder();

// ...

Printer.getInstance().print(receipt.ToString(), (int) Printer.TextAlign.Left, 0, 24, false);

Printer 类是从 DLL 导入的。应用程序在 print 行上抛出非托管异常并崩溃。但是当我将代码更改为此时

var receipt = new StringBuilder();

// ...

var str = receipt.ToString();
Printer.getInstance().print(str, (int) Printer.TextAlign.Left, 0, 24, false);

一切正常。 StringBuilder 的计算怎么可能影响流程?

这是我的 Printer.dll 中的方法(已反编译)

public int print(string text, int textAlign, int fontWeight, int fontSize, bool endLineFeed)
{
open();

Int32 prnReturn;
Printer.Prn_SetLang(1);
Printer.PRN_SetFont((byte) fontSize, (byte) fontSize, 0);
Printer.PRN_SetAlign(textAlign);
Printer.PRN_SetBold(fontWeight);
Prn_String(text.TrimEnd());
prnReturn = PRN_PrintAndWaitComplete();

if(endLineFeed)
printEndingLineFeed();

close();

return prnReturn;
}

public void open()
{
PRN_Open();
}

public void close()
{
PRN_Close();
}

private void printEndingLineFeed()
{
open();
//lineFeed(ENDING_LINE_FEED);
PRN_FeedLine(ENDING_LINE_FEED);
close();
}

下面是它从另一个 DLL 调用的方法。不幸的是,DotPeek 不会对此进行反编译。

[DllImport(PrinterDllName, SetLastError = true, EntryPoint = "PRN_FeedLine")]
public static extern Int32 PRN_FeedLine(Int32 pszData);

[DllImport(PrinterDllName, SetLastError = true, EntryPoint = "PRN_PrintAndWaitComplete")]
public static extern Int32 PRN_PrintAndWaitComplete();

编辑:感谢Kevin Gosse , 我发现问题只存在于 Debug 模式。所以我现在的问题是, Debug模式评估与正常执行有何不同。虽然我知道这可能是题外话,但如果有人可以分享相关文档,我会很高兴。

最佳答案

在 Release模式下,您的代码的两个版本是相同的。

在 Debug模式下,有一个微妙的区别,因为 str 的生命周期将延长到当前方法结束(用于调试目的)。

所以这段代码处于 Debug模式:

var receipt = new StringBuilder();

// ...

var str = receipt.ToString();
Printer.getInstance().print(str, (int) Printer.TextAlign.Left, 0, 24, false);

在 Release模式下等价于此:

var receipt = new StringBuilder();

// ...

var str = receipt.ToString();
Printer.getInstance().print(str, (int) Printer.TextAlign.Left, 0, 24, false);

GC.KeepAlive(str);

当您的字符串被提供给 native 代码时,GC 不再跟踪它。因此,它可能会被收集,这将导致 native 部分出现错误。

理论上,编码器会在使用常见类型(例如字符串)时自动保护您免受此类情况的影响,如下所述:https://learn.microsoft.com/en-us/dotnet/framework/interop/copying-and-pinning?redirectedfrom=MSDN .这是另一个难题,但 .net compact framework 可能具有不同的编码器并且不会自动保护您。不幸的是,很难找到关于该主题的具体文档。

当您反编译该方法时,令我感到惊讶的是, native 调用实际上接收的不是原始字符串,而是 text.TrimEnd()。这意味着原始值的生命周期不应有任何影响(因为 native 代码接收到不同的字符串)。然而,事实证明 .TrimEnd returns the original string when there's nothing to trim .当您添加一个空格时,它开始崩溃,即使使用延长字符串生命周期的代码版本也是如此。那是因为现在您正在延长错误字符串的生命周期(因为 TrimEnd() 将返回一个不同的实例,而该实例将由 native 代码使用)。

我相信在 Release 中运行的代码纯属运气。也许它只是改变了垃圾收集器的时间,你不会遇到那个特定的问题,但它可能会在未来引起问题。出于谨慎,我建议您:

  • 在调用Printer.getInstance().print
  • 之前修剪字符串
  • 在调用 print 之后调用 GC.KeepAlive 修剪后的字符串
  • 如果您想更加安全,可以固定字符串而不是调用 GC.KeepAlive

我希望我能提供的不仅仅是理论,但我相信您遇到了 .net 紧凑框架的特殊性。如果在该主题上有更多经验的人阅读本文并可以提供更多信息,将不胜感激。

关于c# - 将字段作为参数传递与将方法调用作为参数传递有何不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58983040/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com