简体   繁体   English

C# 如果另一个相同类型的泛型更改,泛型变量会更改

[英]C# Generic variable changes if another same type generic changes

I've this class:我有这个 class:

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() + ")";
    }
}

And I have a class with two Pair variables:我有一个带有两个 Pair 变量的 class :

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>> { }

Now if I execute this code:现在,如果我执行此代码:

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

fake.CurrentValue.A = 32;
fake.CurrentValue.B = 53;

In debugging Current Value and Last Value have the same value "32" and "53".在调试时 Current Value 和 Last Value 具有相同的值“32”和“53”。

How can I avoid this?我怎样才能避免这种情况?

Classes are reference types, so when you set LastValue = CurrentValue , that means both LastValue and CurrentValue refer to the same object.类是引用类型,因此当您设置LastValue = CurrentValue时,这意味着 LastValue 和 CurrentValue 都引用同一个object。

If you want Value semantics you should declare your Pair as a struct .如果你想要Value 语义,你应该将你的 Pair 声明为struct This means that an assignment does a copy of the value.这意味着赋值会复制值。 Except ofc there already are a built in type for this: ValueTuple , with some special syntax that lets you declare types like (int A, int B) .除了 ofc 已经有一个内置类型: ValueTuple ,具有一些特殊的语法,可以让您声明类型,如(int A, int B) There is also a regular Tuple<T1, T2> if you do want a reference type.如果您确实需要引用类型,还有一个常规的Tuple<T1, T2>

Also note that I see no way for your example to run, fake.CurrentValue should be initialized to null and crash when accessed.另请注意,我认为您的示例无法运行, fake.CurrentValue应初始化为 null 并在访问时崩溃。 Using a value type would also solve this, since they cannot be null.使用值类型也可以解决这个问题,因为它们不能是 null。

So just change your example to FakeClassWithPair:FakeClass<(int A, int B)> and everything should work as you expect it to.因此,只需将您的示例更改为FakeClassWithPair:FakeClass<(int A, int B)> ,一切都应该按照您的预期工作。

Definitely do not roll your own class for a pair if you want value semantics.如果您想要价值语义,绝对不要为一对滚动您自己的class Use the built-in value tuple, defined as (T a, V b) .使用定义为(T a, V b)的内置值元组。

Also if your content of FakeClass is cloneable then you should take advantage of that (for example arrays are cloneable).此外,如果您的FakeClass内容是可克隆的,那么您应该利用它(例如 arrays 是可克隆的)。 So the assignment in Execute() would check if the current value implements ICloneable and proceeds accordingly.因此Execute()中的分配将检查当前值是否实现ICloneable并相应地继续。

See this example code with output.请参阅此示例代码与 output。 The first example with fk variable is defined by FakeClass<(int,int)> and the second example with fa variable is defined by FakeClass<int[]> .带有fk变量的第一个示例由FakeClass<(int,int)>定义,带有fa变量的第二个示例由FakeClass<int[]>定义。 Some fun code is added to display arrays as list of vales in ToString() in order to mimic the behavior of tuples with arrays.添加了一些有趣的代码以将 arrays 显示为ToString()中的值列表,以模拟具有 arrays 的元组的行为。

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]
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM