【问题标题】:Instanced extension methods? C#实例化扩展方法? C#
【发布时间】:2012-08-28 08:40:51
【问题描述】:

所以我想为 api 已经密封的类型创建一个扩展方法。

如果您了解扩展方法,以下内容应该很熟悉。

private static List<Member> memberList = new List<Member>();

public static List<Member> GetMemberList(this GameObject go)
{
    return memberList;
}

注意,要声明一个扩展方法,它需要是静态的,因为它需要是静态的,所以我通过 GameObject 类型访问的 List 必须是静态的。 我希望每个游戏对象都有自己的成员列表。但是我很确定,因为这是一个静态字段,每个实例化的 GameObject 都将指向同一个静态成员列表。

那么我的假设是否正确?如果是这样,什么可能是替代方案?我想避免将 GameObject 放在也包含 memberList 的包装类中,因为 api 只允许在运行时检测和操作 GameObject。有一些方法可以通过 gameObject 反向引用包装类,但这会增加我想要避免的代码的复杂性。

【问题讨论】:

  • 您将在GameObject 实例上调用GetMemberList,它将返回该实例的列表。我不认为我看到了问题。 (扩展方法是静态的,必须声明为静态类型,但“扩展”this参数是一个实例。)
  • 这是我的问题,如果这会发生。我也刚刚从另一个网站上读到这个“扩展方法是一种特殊的静态方法,但它们被称为扩展类型的实例方法。”但是静态成员列表呢?它是从扩展方法访问的静态字段。
  • @DanJ:问题是成员列表不是游戏对象的一部分。
  • 扩展方法并没有什么特别之处,除了它们可以被视为你正在扩展的类上的方法。它们仍然是普通的旧静态方法,它们接触的任何静态方法仍然是全局的。
  • @Guffa 啊,我误会了。我读到“我希望每个游戏对象都有自己的成员列表”,然后忽略了静态 memberList 的存在。

标签: c# extension-methods


【解决方案1】:

是的,如果你想在游戏对象之外保留一些东西并通过扩展方法访问它,它必须是静态的。

您可以使用字典将一个成员列表映射到每个游戏对象:

private static Dictionary<GameObject, List<Member>> memberLists = new Dictionary<GameObject, List<Member>>();

public static List<Member> GetMemberList(this GameObject go) {
  return memberLists[go];
}

【讨论】:

  • +1 虽然我想我会提到 - 如果你这样做,请小心,因为静态字典会将所有项目以及 GC 可访问的 GameObject 实例保存在列表中,所以它们永远不会得到GCed。如果您使用这种方法,请确保您的 GameObject 实例正确删除自己,否则您可能会遇到内存问题。
  • 这个很有意思,我马上去试试。还有@Reed Copsey,非常好。当我找出正确的解决方案时,我会把它留在我的。
  • 还有 GC 问题,它会在第一次调用时抛出 KeyNotFoundException,它不是线程安全的。
  • @JonHanna:我认为您的意思是该方法不会找到您没有放入字典中的键,这很明显。只要将每个游戏对象的列表添加到字典中,它就不会抛出任何异常。线程安全是一个好点。如果一次从多个线程访问,则需要添加一些同步。
  • 是的,但是要将新列表放入字典中,您需要知道您需要将新列表放入字典中。除非所有 GameObject 都来自同样添加新列表的单个工厂方法,否则这也需要在此调用中处理。
【解决方案2】:

看来您正在使用 Unity3。 UnityAnswers 网站上有一个相关的答案可能会有所帮助:http://answers.unity3d.com/questions/22493/unity-3-sealed-class-gameobject-.html

似乎应该有办法使用内置脚本系统在 Unity 框架中附加您想要的固有行为。

【讨论】:

  • +1 因为是通灵者!,我完全忘记了组件。我会在统一答案中问这个问题,但我认为这是一个纯粹的 C# 问题。
  • 一个很好的解决方案最终是使用 List 的实例版本将组件附加到游戏对象,然后使用扩展方法作为读取友好的方式来访问它。感谢@Jan 指出扩展方法只能扩展行为而不是数据。这帮助我意识到了这一点。
  • 因此,谷歌搜索密封类 GameObject 是寻找通灵者的良好起点。
【解决方案3】:

是的,你是对的。如果您有静态方法,则该类的所有实例共享相同的数据。呼叫return memberList; 是非法的。它与return this.memberList; 相同,this 在静态方法中不可用。相反,您必须调用该类:return GameObject.memberList;。但我知道您不是在寻找这个解决方案。

扩展方法旨在创建额外的行为。如果您想创建额外的数据,使用继承扩展 GameObject 类是正确的选择。

或者,您可以使用Dictionary&lt;GameObject, List&lt;Member&gt;&gt; 形式的字典附加成员列表。但我个人更喜欢如下所示的构图:

public class myGameObject
{
    public List<Member> memberList { get; set; }
    public GameObject go { get; set; }
}

【讨论】:

  • 这个答案有几个问题,1 月 1 日/ return memberList 是合法声明(不是调用),但会返回类静态成员,而不是实例成员。绝对不等于返回this.memberList。 2/ 扩展密封类是不可能的,但是使用组合的包装类可以,但是如果它被一个需要 GameObject 实例的框架使用,你不会有好的结果。
  • 不幸的是,从游戏对象继承无法完成,因为它已从 api 中密封。为了在过去解决这个问题,我创建了与您所展示的类似的包装类。但它增加了很多我想避免的额外复杂性。
【解决方案4】:
private static ConditionalWeakTable<GameObject, List<Member>> dict = new ConditionalWeakTable<GameObject, List<Member>>();

public static List<Member> GetMemberList(this GameObject go)
{
  return dict.GetOrCreateValue(go);
}

ConditionalWeakTable 管理对象的生命周期,因为它使用弱引用。因此,如果没有其他对它的实时引用,它不会阻止 GC 收集 GameObject,这也将允许收集 List&lt;Member&gt;

它是线程安全的,但这假设您希望起点是一个空列表(如果没有当前值,默认构造函数在GetOrCreateValue 中调用)。如果你想要一个不同的起点,你的线程问题会变得更加复杂。

【讨论】:

  • 这也是一个不错的选择,我有很多选择可以尝试。
  • Tetsujin no Oni's 似乎是最合理的赌注,虽然我自己对团结一无所知。
【解决方案5】:

扩展方法只是“看起来”像实例方法的静态方法。

但是,它们不会添加静态方法没有的任何附加功能,它只是为了易于使用、维护和可读性。扩展方法也不能访问受保护/私有成员。

如果GameObject 实际上没有密封(即它没有sealed 关键字),那么您可以编写一个继承GameObject 的类来访问其受保护的方法/字段/属性。仅当您自己是构建这些对象的人时,这才有效。

【讨论】:

  • 是的,不幸的是,从 GameObject 继承是禁止的,我无权更改它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-04
  • 1970-01-01
  • 2015-01-28
相关资源
最近更新 更多