【问题标题】:C#: Pass a struct of an object in by reference or the entire object normally?C#:通过引用或整个对象正常传递对象的结构?
【发布时间】:2012-07-10 13:19:34
【问题描述】:

如果我有一个对象,我只需要访问它的特定部分,我应该只传递那个特定部分还是应该传递整个对象?

就我而言,我正在使用 XNA 使用 C# 构建游戏,我需要访问“DrawLayer.cs”中的 Camera 类的“边界”,该类绘制单个图层(假设有多个图层由于视差)游戏到屏幕上。相机的“边界”作为以下属性存在:

    public Rectangle Bounds { get { return onScreenCamera.Bounds; } }

该游戏是一款多人游戏,有一个摄像头,可以根据玩家的动作在玩家之间跳转,因此“onScreenCamera”引用了当前需要使用的摄像头。此外,根据您的播放器类型,相机的功能会有所不同,因此我制作了一个接口 ICameraSetup.cs,我正在讨论的相机实现了该接口。这是ICameraSetup接口:

interface ICameraSetup
{
    Vector2 Position { get; }
    Rectangle Bounds { get; }

    void Update(GameTime pGameTime);
}

使用 DrawLayer.cs,我想这样写构造函数头(注意 Rectangle 是一个结构,这应该意味着它是一个值类型):

    public DrawLayer(ref Rectangle pBounds)

然后用构造函数调用,是这样的:

    new DrawLayer(ref levelState.Camera.Bounds)

这只会将相机的边界传递给 DrawLayer.cs,但我似乎收到了这个错误:“The Property Game.Camera.ICameraSetup.Bounds has no setter。”问题是,我不希望接口为 Bounds 声明一个 setter,因为唯一应该能够设置边界的类是 Camera 类,而 Bounds 的设置方式是通过上面提到的 camera 类中的私有变量,称为“onScreenCamera”。

但是,即使我将 Set 添加到 Bounds 属性,我仍然在 DrawLayer.cs 构造函数调用时收到此错误:“'ref' argument is not classification as a variable.”

现在,我知道一种可行的解决方案是将整个相机传递到 DrawLayer.cs 并从那里使用边界,如下所示:

    public DrawLayer(ICameraSetup pCamera)

然后

    new DrawLayer(levelState.Camera)

然后使用 Camera.Bounds,但这似乎没有必要,因为我需要的只是该类中的边界。

我可能遇到的另一个问题是我对传递引用的理解。我理解当您需要一个值类型引用以随时更新您传递给它的类之外的值所做的任何更改时,它很有用,但我也读到它是相反的:使用它可以在最初声明的位置之外更改值类型并使其保持不变。

那么我在将相机的边界通过引用传递到 DrawLayer 时是否犯了一个错误?

【问题讨论】:

  • AFAIK XNA 通过引用传递结构的唯一原因是速度,仅此而已。

标签: c# parameters pass-by-reference


【解决方案1】:

您不能通过引用传递属性。这是有道理的,因为属性不是变量 - 它们由编译器实现为 getter/setter 方法。

但是,您可以将 Bounds 属性更改为字段,或创建一个局部变量来保存 Bounds 的值,然后可以使用 'ref' 关键字传递它。

通过 'ref' 传递某些内容的主要原因是,如果您需要能够更改变量的任何部分,包括用新实例替换变量,并且您希望这会反映在调用代码中。

如果您只想访问 Bounds,请不要使用 'ref'。如果 Bounds 是一个结构,并且出于性能原因不想复制它,请考虑将 Bounds 设为一个类。

【讨论】:

  • 这将使错误静音,但不会使 OP 正常工作。他想存储对值类型的引用,这是不可能的。这是一个设计问题。
【解决方案2】:

您忽略了第一个错误:

属性、索引器或动态成员访问不能作为 out 或 ref 参数传递。

此外,以这种方式通过引用传递参数意味着可以设置它,但不能设置,因为您没有为Bounds 属性定义公共设置器。所以,我们有两个问题。

我很困惑;你为什么试图通过引用来传递它?您希望存储对此对象的引用,以便以后可以更改(仅供参考:您不能)?老实说,对我来说,这听起来像是一个糟糕的设计。这似乎不是性能问题,所以我建议调整您的设计。

当您遇到此类奇怪且笨拙/无法正常工作的事情时,通常意味着您手头有设计问题。不要试图通过引入更多设计问题来解决这个问题。

【讨论】:

  • 哦,边界是一个矩形,它是一个结构,它是一个值类型,但我想我已经将矩形封装在一个属性中,所以我不能通过引用传递它。我的意图是让 DrawLayer 类包含相机边界的最新参考,这是糟糕的设计吗?我想我也可以让 DrawLayer 类中的 Update 方法在每次更新(每秒 60 次)时获取 Camera Bounds,但是对 Camera Bounds 进行自我更新引用不是更容易吗?
  • @BlakeThiessen:对,但即便如此;你为什么要?无论如何,您都不能存储对结构的引用。你认为这是必要的性能优化吗?我几乎可以肯定这不是你的情况,尽管我可能是错的。你测试过吗?
  • @BlakeThiessen:这就是我的想法(根据您的编辑),但无论如何您都不能存储对值类型的引用。你想要的是不可能的。重构你的代码。相反,让 DrawLayer 持有对相机的引用,或者在您调用 DrawLayer 上的方法时从外部提供视图(即DrawSomething(Whatever foo, Rect Bounds);)。您可以有很多方法,但不是你提议的方式。
  • 嗯,有道理。那么,这些解决方案中的任何一种通常被认为是更好的实践还是更快?将包含感兴趣变量的对象传递给构造函数与将感兴趣的变量传递给该对象中的方法?
  • @BlakeThiessen:好吧,在这种情况下,将它传递给构造函数是没有意义的(这里的速度真的不是问题),因为它将是一个副本并且很快就会过时。这实际上取决于您需要信息的方式和地点。就像我说的,您可以存储对Camera 的引用,您可以将其作为方法的参数,您可以重构整个事物。这是你的设计,做对你的项目最有意义的事情。
猜你喜欢
  • 2011-07-06
  • 2011-09-27
  • 2018-04-03
  • 2014-02-08
  • 1970-01-01
相关资源
最近更新 更多