如果另一个相同类型的泛型发生变化,C#泛型变量也会发生变化



我有这个类:

public class Pair<T, V>
{
public T A = default;
public V B = default;
public Pair()
{
A = default;
B = default;
}
public Pair(T a, V b)
{
A = a;
B = b;
}
public override bool Equals(object obj)
{
Pair<T, V> other = obj as Pair<T, V>;
return A.Equals(other.A) && B.Equals(other.B);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public override string ToString()
{
return "Pair: (" + A.ToString() + " , " + B.ToString() + ")";
}
}

我有一个类,有两个Pair变量:

public class FakeClass<T>
{
public T LastValue { get; protected set; } = default;
public T CurrentValue = default;
public void Execute() 
{
LastValue = CurrentValue
}
}
public class FakeClassWithPair : FakeClass<Pair<int, int>> { }

现在,如果我执行这个代码:

FakeClassWithPair fake = new FakeClassWithPair();
fake.CurrentValue.A = 2;
fake.CurrentValue.B = 5;
fake.Execute();
fake.CurrentValue.A = 32;
fake.CurrentValue.B = 53;

在调试中,当前值和最后值具有相同的值";32〃;以及";53〃;。

我该如何避免这种情况?

类是引用类型,因此当您设置LastValue = CurrentValue时,这意味着LastValue和CurrentValue都引用相同的对象。

如果想要值语义,则应将Pair声明为结构。这意味着赋值将复制该值。除了ofc之外,已经有一个内置的类型:ValueTuple,它具有一些特殊的语法,可以声明(int A, int B)之类的类型。如果确实想要引用类型,那么还有一个常规的Tuple<T1, T2>

还要注意,我认为您的示例无法运行,fake.CurrentValue应该初始化为null,并在访问时崩溃。使用值类型也可以解决这个问题,因为它们不能为null。

因此,只需将您的示例更改为FakeClassWithPair:FakeClass<(int A, int B)>,一切都应该如您所期望的那样工作

如果您想要值语义,则绝对不要为一对滚动自己的。使用内置的值元组,定义为(T a, V b)

此外,如果FakeClass的内容是可克隆的,那么您应该利用这一点(例如,数组是可复制的(。因此,Execute()中的赋值将检查当前值是否实现ICloneable,并相应地进行。

请参阅带有输出的示例代码。具有fk变量的第一个示例由FakeClass<(int,int)>定义,具有fa变量的第二个示例由FakeClass<int[]>定义。添加了一些有趣的代码,以在ToString()中将数组显示为值列表,从而模拟具有数组的元组的行为。

public class FakeClass<T>
{
public T LastValue { get; protected set; } = default(T);
public T CurrentValue = default(T);
public void Execute()
{
if (CurrentValue is ICloneable cloneable)
{
LastValue = (T)cloneable.Clone();
}
else
{
LastValue = CurrentValue;
}
}
public override string ToString()
{
if (typeof(T).IsArray)
{
object[] last, current;
Array cv = CurrentValue as Array;
if (cv != null)
{
current = new object[cv.Length];
cv.CopyTo(current, 0);
}
else
{
current = new object[0];
}
Array lv = LastValue as Array;
if (lv != null)
{
last = new object[lv.Length];
lv.CopyTo(last, 0);
}
else
{
last = new object[0];
}

return $"Current=[{string.Join(",",current)}], Last=[{string.Join(",",last)}]";
}
return $"Current={CurrentValue}, Last={LastValue}";
}
}
class Program
{
static void Main(string[] args)
{
var fk = new FakeClass<(int a, int b)>();
fk.CurrentValue = (1, 2);
Console.WriteLine(fk);
// Current=(1, 2), Last=(0, 0)
fk.Execute();
fk.CurrentValue = (3, 4);
Console.WriteLine(fk);
// Current=(3, 4), Last=(1, 2)
var fa = new FakeClass<int[]>();
fa.CurrentValue = new int[] { 1, 2 };
Console.WriteLine(fa);
//Current=[1,2], Last=[]
fa.Execute();
fa.CurrentValue = new int[] { 3, 4 };
Console.WriteLine(fa);
//Current=[3,4], Last=[1,2]
}
}

最新更新