【问题标题】:C# - make it so only the same instance can modify its properties/call methods?C# - 让它只有同一个实例可以修改它的属性/调用方法?
【发布时间】:2017-10-20 17:53:25
【问题描述】:

我正在使用 MonoGame 框架制作游戏引擎。由于这是一款多人游戏,其他玩家需要知道其他玩家的某些信息,例如位置和速度,通常用于 AI 反应。

我即将为玩家操作实现 Lua 脚本。但是,我目前想不出办法让玩家无法修改其他玩家的信息,例如位置和姓名。

例如,假设我在 C# 中有以下属性:

public Vector2 Pos { get { return pos; } private set { this.pos = value; } }
private Vector2 pos;

看起来不错,对吧?不完全是。假设我有一个函数返回最近的敌人GetNearestEnemy(),它返回另一个玩家。因为 Player 类可以访问它自己的属性,所以我可以在其他方法中轻松地做这样的事情:

public void DoStuff()
{
    Player otherPlayer = GetNearestEnemy();
    otherPlayer.Pos = new Vector2(34,151);
}

所以我认为“也许我可以将实例作为参数传递。”

public void SetPos(Player instance, Vector2 pos)
{
    if (instance != this)
        return;
    else
        this.pos = pos;
}

...并且始终将“this”作为参数传递。但是,这也有同样的问题:我可以很容易地将GetNearestEnemy() 传递给该方法,并且可以设置敌人的位置。

这是我试图阻止的行为类型。有没有办法最好:

  1. 获取调用该方法的实例?
  2. 在调用对象不知情的情况下隐式设置参数?

或者是否有其他我没有看到的方法来处理它?

【问题讨论】:

  • 我不认为otherPlayer.Pos = new Vector2(34,151); 应该工作 - 你真的测试过吗?
  • 不,这里没有语言支持来处理您想要的内容。据推测,既然你正在编写这个类,你就可以确保它不会做不应该做的事情。
  • @MetaColon 是的。由于 DoStuff 是 Player 中的一个方法,它可以访问 Player 类所做的一切。
  • 使GetNearestEnemy 返回源引用的克隆。这样一来,任何修改都会在克隆上执行,并且无法修改源引用。
  • 假设同一个程序员做这两个类,这只是设计的一部分。如果有两个相互竞争的程序员,语言特性不会阻止他们在同一个进程中访问状态。然而,CLR 具有一些隔离功能,例如应用程序域(仅限完整的 .NET Framework)。 (参见 .NET 容器)

标签: c# .net lua monogame


【解决方案1】:

很遗憾没有。

没有您想要的语言支持。

让我具体说明您想要什么,以便您了解我的回答。

您的问题基本上是这样的:鉴于我有一个对象的 两个 实例,并且我在该对象中有具有私有设置器的属性,是否有任何语言支持来确保该实例 #1无法更改实例 #2 的此私有信息?

答案是否定的。

这将编译并“工作”:

public class Test
{
    public void TestIt(Test t)
    {
        t.Value = 42;
    }

    public int Value
    {
        get;
        private set;
    }
}

...
var t1 = new Test();
var t2 = new Test();
t1.TestIt(t2); // will "happily" change t2.Value

基本上,有责任确保在您不希望发生时不会发生这种情况。没有语言或运行时支持可以防止这种情况发生。

您可以使用的访问修饰符是:

  • public:任何人都可以访问这个
  • private:只有类型可以访问这个
  • protected:只有类型,或者类型的后代,可以访问这个
  • internal:任何类型在同一个程序集中都可以访问这个
  • internal protected:任何类型在同一个程序集中 后代,都可以访问这个

除此之外,您别无选择。所以“只有同一个instance可以访问这个”不作为访问修饰符存在。

【讨论】:

  • 那真是太糟糕了。经过一番思考,我想我应该做的不是在这样的方法中返回整个播放器实例,而是返回一个包含他们想要查看的值的只读副本的结构。这样,脚本中的玩家对象可以从其他玩家那里获得他们想要的信息,但他们不能设置它们。感谢您的信息!
  • 您实际上可以使用闭包来实现这一点,但您需要使用IDictionary<string, object> 的一些变体,例如expando 对象。您将没有类型系统支持,但可以使用闭包对实例级隐私进行建模。
【解决方案2】:

如果这是属性并且您担心从其他地方设置:

public Vector2 Pos { get { return pos; } private set { this.pos = value; } }
private Vector2 pos;

这行不通。所以你不必担心:

Player otherPlayer = GetNearestEnemy();
otherPlayer.Pos = new Vector2(34,151); // <--- no this will not work

该属性只能在类中设置。

【讨论】:

  • 这就是我要说的。后者可以在类内完成,这是我不希望该类的其他实例发生的情况。
【解决方案3】:

那是行不通的。如果Pos 是一个带有私有设置器的属性(因为它是),那么他们可以更改它的唯一方法是从otherPlayer 中调用一个公共方法。类似于otherPlayer.SetPos(new Vector2(34,151)),其中SetPos() 是:

public void SetPos(Vector2 NewPos)
    { Pos = NewPos; }

【讨论】:

  • 它确实有效,因为 DoStuff 是 Player 中的一种方法。因此它可以访问 Player 类所做的一切。
【解决方案4】:

发布的原始场景可以使用接口来处理,例如IPeerPlayer 只公开其他玩家应该看到的内容并隐藏其他属性(即其他属性不会在 IPeerPlayer 界面中。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多