【问题标题】:Why a property can not be passed as out parameter?为什么属性不能作为输出参数传递?
【发布时间】:2011-01-27 17:18:50
【问题描述】:

例如:

    int? qID= null;

    answer.QuestionID = int.TryParse(lblID.Text, out qID.Value) ? qID : null; //Error: Property or Indexer may not be passed as an out ot ref parameter.

从微软文档中它说:

“作为out参数传递的变量不需要初始化。但是,在方法返回之前必须为out参数赋值。”

然后:

"属性不是变量,不能作为out参数传递。

那么,在底层 .net 平台设计中禁止通过 out 设置对象属性的原因是什么? out 的值也不必是引用对象——完全合法地使用值类型。那为什么不呢?

【问题讨论】:

  • Max,有 any 保证该属性不只实现 getter 吗?例如,公共字符串 MyName {get;private set;}。你不能,因此框架设计非常有意义。
  • @code4life:你的说法没有意义,编译器可以在编译时检查属性是否已经有一个setter。
  • 我问你的问题是,为什么你使用Nullable<int> 作为qID 而int 就可以了?
  • @Lazarus 这只是为了展示问题,代码与现实世界无关。但你是对的,这是有道理的。
  • 如果您考虑抛出异常的属性,则会出现另一个有趣的问题。然后一个 TryParse 可能会成功,但 Property 可能会阻塞,它会为一些令人困惑的错误让路。或者,如果调用的方法设置属性两次(首先将其设置为默认值,并且可选地设置其他值),例如 TrySomething 愉快地假设其 out 参数可以设置为 null,它可以设置但属性不允许它。

标签: c# .net parameters


【解决方案1】:

这在 VB 中有效,但在 C# 中无效... VB 有效地为您创建了一个临时局部变量,调用传入局部变量作为参数的方法,然后将属性设置为局部变量的值. C# 通常不会为您隐藏那种东西。

方法本身需要一个变量作为out参数。它必须有一个可以写入值的存储位置。不是一个属性,也不是它需要调用的任何东西:只是一个存储位置。属性不满足该要求。所以编译器在方法中没有什么可以做的。

因此,编译器必须根据 VB 使用临时变量来伪造它,或者根据 C# 禁止它。我个人更喜欢 C# 方法 - 否则它看起来 好像每次方法为 out 参数分配一个值时,都会设置属性 - 当然不是这样。

【讨论】:

  • 这是真的,但是在他的示例中,即使在 VB 中,如果您通过引用传递 qID.Value,它将编译并在运行时工作,但变量不会使用新值更新
【解决方案2】:

属性只是一对名为 get_Somethingset_Something 的函数。
out 参数引用字段或变量;传递一对函数没有任何意义。

VB.Net can pass properties as ByRef parameters;编译器生成一个临时变量,并在调用该方法后将属性重新分配给该变量。

但是,即使是 VB.Net 也无法处理您的情况,因为 Nullable<T>.Value 属性是只读的。

【讨论】:

  • 是的,通过 out 设置属性应该不是问题。我想我正在寻找你为什么不能这样做的解释。
  • @Max:一对函数不是指针,不能做成指针。
  • @Max:是什么让你认为它不应该是一个属性?鉴于方法本身需要一个存储位置 - 一个变量 - 会做什么来满足这个要求?
  • @Jon,如果 VB 使用 temp 局部变量来存储 out 的输出,那为什么不符合 c# 语义?
  • @Max:因为那会给人错误的印象,IMO。它使它看起来就像每次方法为其输出参数分配一个值时,都会设置该属性。事实并非如此,并且很容易引起混乱。我不知道这在 VB 中是否有历史先例,但我个人认为这是一个错误。
【解决方案3】:

因为属性是编译器生成的getset 方法的语法糖。

【讨论】:

    【解决方案4】:

    属性只是一对访问器方法的语法糖,所以你在这里实际做的是调用一个方法并将结果值作为引用传递。显然这个值不是一个变量,所以它不能被绑定。

    考虑一个带有属性Bar 的类型Foo;将该属性用作out 参数本质上类似于:

    Foo foo = new Foo();
    SomeFunction(out foo.get_Bar());
    

    显然,不能将值分配给foo.get_Bar()

    【讨论】:

    • 我仍然不明白为什么不如果有属性的设置器,可以创建一个本地临时变量来存储该值。如果 vb 这样做,为什么 c# 不能?跨度>
    【解决方案5】:
    int qID;
    
    if (int.TryParse(lblID.Text, out qID))
    {
      answer.QuestionID =  qID;
    }
    else
    {
      answer.QuestionID = null;
    }
    

    这是实际的实现:

    [System.Security.SecuritySafeCritical]  // auto-generated
        internal unsafe static Boolean TryParseInt32(String s, NumberStyles style, NumberFormatInfo info, out Int32 result) {
    
            Byte * numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes]; 
            NumberBuffer number = new NumberBuffer(numberBufferBytes);
            result = 0; 
    
            if (!TryStringToNumber(s, style, ref number, info, false)) {
                return false; 
            }
    
            if ((style & NumberStyles.AllowHexSpecifier) != 0) {
                if (!HexNumberToInt32(ref number, ref result)) { 
                    return false;
                } 
            } 
            else {
                if (!NumberToInt32(ref number, ref result)) { 
                    return false;
                }
            }
            return true; 
        }
    

    【讨论】:

    • 那不会编译。 int 不是 int?
    • @K Ivanov,是的,这行得通,但你为什么不能仍然设置属性 - 这是我的问题。
    • .Value 是只读属性,不能设置,msdn.microsoft.com/en-US/library/ydkbatt6(v=VS.100).aspx
    • TryParse 的实现完全无关。
    • 怎么不相关?它在一开始就将传递的参数设置为 0,这假设它不是只读的
    猜你喜欢
    • 2011-10-27
    • 1970-01-01
    • 2021-07-18
    • 2017-11-19
    • 1970-01-01
    • 1970-01-01
    • 2016-08-16
    • 1970-01-01
    相关资源
    最近更新 更多