【问题标题】:Conflict between passing object by reference and cs1690通过引用传递对象和cs1690之间的冲突
【发布时间】:2015-07-10 10:24:02
【问题描述】:

此代码已更新。 机器人.cs:

 struct state systemcheck()
 {
 state stateInfo = new state();
 public double x,y,z; 
 }

Main.cs:

public state stateInfo;
private readonly Sub cpnew;
public Main()
{
InitializeComponent();
cpnew = new Sub(this);
}

子.cs:

public state systinfo;
private readonly Main main;
public Sub(Main main)
{
InitializeComponent();     
this.main = main;
systinfo = this.main.stateInfo; 
} 

这里,systinfo.X 提供了一个空值。但是mainfrm.stateInfo.X 提供了正确的值,但会引发 marshal-by-reference 类警告。 初始化 systinfo 的正确方法是什么? state 的值是从外部连接的机器人获得的。

【问题讨论】:

  • 请提供一个完整、有效的例子。 public void state systinfo 显然无效,并且您的示例的其他方面很难理解。如果您可以遵循 .NET 命名约定,也会更清楚。
  • public Sub(ref Main main) 中删除ref。用 C++ 的说法,你正在用你的代码传递一个指向 Main 的指针,这不是你想要在这里做的。
  • @DavidArno: public Sub(ref Main main) 是在我实现 `cpnew = new Sub(ref main); 时自动构建的` 此外,当我尝试修改它时它会引发错误。

标签: c# object ref


【解决方案1】:

您提出的要求是件好事 - 请记住始终遵循警告,除非您完全知道自己在做什么。在这种情况下,您肯定知道自己在做什么。

Formclass - 所有类都是 .NET 中的引用类型。这有几个含义,其中一个与此处高度相关 - 它们总是通过引用传递。换句话说,当您使用Main main 作为参数时,您已经在传递一个引用(类似于在C 中传递一个指向Main 的指针)。

使用ref,您将引用传递给引用。这允许您从方法内部修改外部引用。这不是你想要的——作为一个简单的例子,它允许你编写一个增量方法:

public void Increment(ref int value)
{
  value = value + 1;
}

如果您没有使用ref,这只会修改value 的本地值。使用ref,它会修改调用者中的本地

在你的情况下,正确的代码会更接近

public state stateInfo;
private readonly Sub cpnew;
public Main()
{
    InitializeComponent();

    cpnew = new Sub(this);
}

表格2:

public state systinfo;
private readonly Main main;
public Sub(Main main)
{
    InitializeComponent();      
    this.main = main;
    systinfo = mainfrm.stateInfo; 
} 

那么,警告告诉你什么? Form 继承自 MarshalByRefObject。这意味着您实际上可能没有传递真正的 Form 对象 - 例如,您完全有可能只有一个 代理 到远程计算机上的 Form。这意味着对类型的所有调用都通过代理自动编组,在 actual 实例上执行,并返回结果。因为statestruct(我打赌这是因为你不理解C# 的struct 和C 的struct 之间的区别),它是一个值类型——如果你确实持有代理而不是真实的实例,这将导致运行时异常。

要绕过警告,您可以先将 state 复制到本地 - 这样可以确保安全(更重要的是,显而易见)。

在从 C++ 切换到 C# 时,您可能会陷入很多的陷阱——它们表面上看起来很相似,但有很多不同之处。尝试在实际使用之前查找一下您尝试使用的任何内容 - 例如,查找 ref 关键字会明显表明您正在创建指向指针的指针,并查找 structclass 会告诉你它们的行为与 C++ 完全不同。小心点。

C# 中的编码惯用变得更加严肃。例如,您通常在需要它们的地方创建子表单和对话框,而不是在构造函数中创建实例并在需要时重用它。当然,循环引用通常是一个糟糕的想法——它是增加代码复杂性的好方法,它使调试和测试变得更加昂贵。进一步的重构也会对循环引用造成更大的伤害。

【讨论】:

  • 感谢您的回复。但问题仍然存在。 systinfo.X 仍然会产生一个空值,mainfrm.stateInfo 虽然会产生输出,但会引发 CS1690 异常(Marshal by reference)。
  • @srivas 为什么不应该是null?你永远不会初始化它。
  • 我做了如下初始化:public Sub(Main main) { InitializeComponent(); this.main = main; systinfo = new state(); systinfo = mainfrm.stateInfo; }
  • @srivas 您可能没有意识到这一点,但您只是创建了一个新实例(很好),将其分配给一个字段(很好),然后立即将其替换为来自 @987654348 的统一实例@.
  • 请推荐正确的初始化对象(systinfo)的方法,它涵盖了由于直接调用(mainfrm.stateinfo)而导致的引用覆盖marshall的方面。我很抱歉我犯了一个巨大的错误。这里mainfrmmain 开启并且相同。
猜你喜欢
  • 2015-03-05
  • 1970-01-01
  • 2017-09-11
  • 2012-10-04
  • 2011-07-06
  • 2012-07-07
  • 2012-01-05
  • 2012-07-16
相关资源
最近更新 更多