从泛型数组返回引用



我有一个类,其中包含ComponentBase列表。Component子类ComponentBase.

public abstract class ComponentBase 
{
public Type Type;
public dynamic Value;
public uint EntityID;
}
public class Component<T> : ComponentBase
{
public new Type Type;
public new T Value;
public new uint EntityID;
}

我需要一种在GetComponentOnEntity方法中检索refComponent<T>.Value的方法,我似乎找不到它。我不太擅长泛型,所以我会把这个问题交给专业人士。

public class ComponentDatabase
{
ComponentBase[] components;
private int componentIndex;
public T AddComponentToEntity<T>(T component, Entity entity) where T : new()
{
var x = component != null ? component : new T();
var comp = new Component<T> { EntityID = entity.Id, Type = typeof(T), Value = x };
components[componentIndex++] = comp;
return x;
}
public ref T GetComponentOnEntity<T>(Entity entity) where T : new()
{
return ref components.Where(x => x.EntityID == entity.Id && x.Type == typeof(T)).First().Value;
}
}

我有一个漫长的夜晚,我觉得有一个简单的解决方案,但我真的找不到。如有任何帮助,我将不胜感激。

从根本上说,代码的问题在于您试图返回对对象的错误成员的引用。当您实际需要具有T类型的成员时,您返回的是dynamic类型的成员。

你原来的例子相当于这个更简单的例子:

class Base
{
public int Id;
public dynamic Value;
public Base(int id, dynamic value)
{
Id = id;
Value = value;
}
public override string ToString() => $"{{Id: {Id}, Value: {Value}}}";
}
class Derived<T> : Base
{
public new int Id;
public new T Value;
public Derived(int id, T value) : base(id, value) { }
}
static void Main(string[] args)
{
Derived<string>[] array = new[]
{
new Derived<string>(0, "Zero"),
new Derived<string>(1, "One"),
new Derived<string>(2, "Two"),
};
ref string valueRef = ref GetElement<string>(array, 1);
valueRef = "Three";
WriteLine(string.Join<Base>(Environment.NewLine, array));
}
private static ref T GetElement<T>(Base[] array, int id)
{
// error CS8151: The return expression must be of type 'T' because this method returns by reference
return ref array.Where(x => x.Id == id).First().Value;
}

问题很简单,您试图返回基类字段,其类型为dynamic而不是T

要解决这个问题,需要强制转换对象,以便从派生类型获得字段:

private static ref T GetElement<T>(Base[] array, int id)
{
return ref ((Derived<T>)array.Where(x => x.Id == id).First()).Value;
}

或者,在你原来的例子中:

public ref T GetComponentOnEntity<T>(Entity entity) where T : new()
{
return ref ((Component<T>)components.Where(x => x.EntityID == entity.Id && x.Type == typeof(T)).First()).Value;
}

现在,所有的说:我敦促你重新考虑设计。可能您省略了一些将基本Value字段初始化为派生Value字段的代码。但即使是这样(这已经足够浪费了),也没有什么可以阻止这些字段在以后被分配不同的值。这不仅是浪费,而且还可能导致错误,因为对象中每个值都有两个副本,特别是当这些值不是只读的时候。

成员隐藏(即在数据结构中使用new关键字)应该不惜一切代价避免。它只会导致混乱,并且几乎肯定会在以后的道路上导致许多难以发现、难以修复的bug。您在这里遇到的问题只是冰山一角,但它是一个很好的例子,说明当一个对象有两个具有相同名称的不同成员时,事情会多么令人困惑。

此外,ref返回值应该非常谨慎地使用,并且只在绝对必要的时候使用(例如,只有使用ref return才能解决关键的性能问题)。当然,你的问题缺少上下文,所以也许你确实有一个很好的理由在这里使用那个特性。但是仅仅基于代码中的名字,在我看来,你可以让代码工作得很好,没有任何奇怪的代码现在(成员隐藏的ref返回值)。

最新更新