在方法中传递对派生对象的引用时出错

Posted

技术标签:

【中文标题】在方法中传递对派生对象的引用时出错【英文标题】:error when passing a reference to a derived object in a method 【发布时间】:2010-12-12 12:40:37 【问题描述】:

在 c# 中,我试图实现一种方法,我可以使用该方法将数据绑定到我传递给它的任何控件(当然,前提是该控件是从 databoundcontrol 对象派生的)

给定方法

 public void CTLBindData(ref DataBoundControl ctl) ... 

尝试将派生控件传递给函数时出现错误 比如下面的代码

DropDownList lister = new DropDownList();  
CTLBindData(ref lister);

产生转换错误

好吧,我可以接受,但以下让我感到困惑(可能是因为我习惯于 c++ 而不是 c#)

CTLBindData(ref (DataBoundControl)lister);

在这种情况下,我得到了错误 "一个 ref 或 out 参数必须是一个可赋值的变量"

为了澄清,Dropdownlist 继承自列表控件,而列表控件继承自 DataBoundControl

这对我来说毫无意义,我应该能够传入任何从数据绑定控件派生的对象。似乎是显式类型转换导致了问题。

关于我做错了什么的任何线索?

直流

【问题讨论】:

@Andrew 的答案是正确的,但是,你确定它必须通过 ref 传递吗?从逻辑上讲,您可能会返回不同类型的不同对象。如果你不打算在 CTLBindData 中为它分配新对象,那么在有和没有 ref 的情况下传递它是没有区别的。 你是说c#默认将对象作为引用而不是值传递吗?对我来说,你可以取回一个不同的对象是不合逻辑的,除非引用比实际引用更接近指针。 使用ref 可以让不同的对象返回。如果您阅读了我的答案中的链接,它的解释比我们任何人在评论中的解释都要好。 【参考方案1】:

使用具有适当类型参数约束的泛型函数:

public void CTLBindData<TControl>(ref TControl ctl)
    where TControl : DataBoundControl 
 ... 

在调用中直接推断出类型,ocnstraint 确保控件是合适的类型。

【讨论】:

【参考方案2】:

在调用方法之前进行转换:

DataBoundControl countrol = (DataBoundControl)lister;
CTLBindData(ref control);

C# 要求任何ref 参数为精确 类型(无多态性),并且该类型的引用必须是可分配的。这就是为什么您必须在单独的步骤中通过显式转换创建引用,以便该方法具有可以为其分配值的正确类型的引用。

有关此主题的更多信息,请参阅 Eric Lippert 的 Why do ref and out parameters not allow type variation?:

如果您有一个带有“X”的方法 那么你必须传递一个表达式 输入 X 或可转换为 X 的东西。 说,派生类型的表达式 从 X. 但是如果你有一个方法 需要一个“ref X”,你必须通过一个 引用 X 类型的变量,句点。 这是为什么?为什么不允许类型 变化,就像我们对非引用调用所做的那样?

【讨论】:

这不是复制控件并丢失其 Listbox 类还是 DataBoundControl countrol = (DataBoundControl)lister;创建对控件的引用? 您只是在创建引用的副本,而不是控件本身的副本。新的引用指向相同的对象,并且没有发生任何转换。 我想我现在明白了。在 c# 中,赋值 (=) 运算符创建对对象的引用,这与 c++ 中创建对象的副本不同,如果要在 c++ 中引用,则必须指定 & 运算符 @DeveloperChris - 正确,在 C# 中,您不能直接使用托管代码中的指针。【参考方案3】:

Andrew Hare 回答得很好。

您的对象必须在内部需要一些东西才能使用 REFOUT。例如:可以为空。

DropDownList lister = new DropDownList();
lister = null;   
CTLBindData(ref lister);

【讨论】:

【参考方案4】:

Andrew Hare 是正确的,但在这种情况下,您甚至可能不想使用 ref。 C# 中的对象已经通过引用传递*。 (与值类型相反,除非您使用 ref 关键字,否则值类型不会通过引用传递。)我能想到的情况很少,您实际上需要以这种方式传递引用类型。如果没有ref,您的原始代码应该可以正常工作。

**不是真的,但如果你来自非 C# 背景,这样更容易理解。引用实际上是按值传递的。 There is an excellent article 了解这一切是如何运作的。*

【讨论】:

虽然我同意这里可能不需要ref,但重要的是要区分对象不是通过引用传递的。相反,对象引用是按值传递的。 确实如此。我在回答中添加了说明。

以上是关于在方法中传递对派生对象的引用时出错的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中通过引用或值传递对象

虚函数总结

java 值传递 数组传递

Java方法中传递数组与改变数组值的问题

Java方法中传递数组与改变数组值的问题

如何在 nib 中传递对对象的 init 方法的引用?