My implementation of Swap<T> does not work?

三世轮回 提交于 2020-11-30 02:03:53

问题


So I write below quick code that should be working for refernce and struct types, but first one does not do anything useful at all and second is useless for List<int>. Can anyone point out bugs and design issues in below code?

public static void Swap<T>(T o1, T o2) where T : class, IComparable<T>
{
    if (o1.CompareTo(o2) != 0)
    {
        var temp = o1;
        o1 = o2;
        o2 = temp;
    }
}

public static void Swap<T>(ref T o1, ref T o2) where T : struct, IComparable<T>
{
    if (o1.CompareTo(o2) != 0)
    {
        var temp = o1;
        o1 = o2;
        o2 = temp;
    }
}

Why I'm trying to do so: I've read some SO questions about why there is no Swap in C#. Common claim is Swap is not useful and you should just use temp obj to do it. However, I am still not convinced. I am writing my code now and it uses a lot of swap. It looks really uncomfortable to have same code snippet spreaded everywhere. It is still annoying to have to have same lines in more than one method but much better for my projects.

UPDATE:

Based on all input, I realise how little I know about C# and have produced a correct and concise solution:

public static void Swap<T>(ref T o1, ref T o2)
{
        var temp = o1;
        o1 = o2;
        o2 = temp;
}

Two thoughts:

  1. Is it useful to check if o1 and o2 are reference-equal in terms of performance? I guess it is not a very bad idea, but then should I use object.ReferenceEquals()? If yes, and if T is primitive type, does it invoke boxing?

  2. Because ref keyword is used, we can't pass in elements of List. I write an extension method to swap List elements:

    public static void SwapElements<T>(this IList<T> list, int a, int b)
    {
        if (a < 0 || b < 0 || a >= list.Count || b >= list.Count || a > b)
            throw new ArgumentOutOfRangeException();
    
        if (a == b) 
            return;
    
        var t = list[a]; list[a] = list[b]; list[b] = t;
    
        return list;
    }
    

回答1:


The first version, with reference types (the constraint where T : class), is clearly missing ref modifiers on the two parameters. Assigning new references to o1 and o2 changes absolutely nothing for the caller when the parameters are not passed "by ref".

So there is really no reason to have separate methods (by the way, two overloads are not allowed to have the same signatures, illegal C#!) for reference types and value types in this case.

Also, it is unclear why you do not wish to swap just because CompareTo gives 0. It can still matter to swap the references; it could still be distinct instances.

You are probably confusing two unrelated concepts: (1) Reference types (class) and value types (struct). (2) ByRef parameters (ref or out keyword) and value parameters (neither ref nor out present).


EDIT: You could test your understanding with this example:

class MyClass
{
  internal int Field;
}
struct MyStruct
{
  internal int Field; // NB! Many people consider mutable structs evil
}
static class Test
{
  static void Main()
  {
    var a = new MyClass();
    ChangeFieldOfMyClass_ByVal(a);
    Console.WriteLine(a.Field);

    var b = new MyStruct();
    ChangeFieldOfMyStruct_ByVal(b);
    Console.WriteLine(b.Field);

    var c = new MyClass();
    ChangeFieldOfMyClass_ByRef(ref c);
    Console.WriteLine(c.Field);

    var d = new MyStruct();
    ChangeFieldOfMyStruct_ByRef(ref d);
    Console.WriteLine(d.Field);


    var e = new MyClass();
    AssignToParameterMyClass_ByVal(e);
    Console.WriteLine(e.Field);

    var f = new MyStruct();
    AssignToParameterMyStruct_ByVal(f);
    Console.WriteLine(f.Field);

    var g = new MyClass();
    AssignToParameterMyClass_ByRef(ref g);
    Console.WriteLine(g.Field);

    var h = new MyStruct();
    AssignToParameterMyStruct_ByRef(ref h);
    Console.WriteLine(h.Field);
  }

  static void ChangeFieldOfMyClass_ByVal(MyClass p)
  {
    p.Field = 42;
  }
  static void ChangeFieldOfMyStruct_ByVal(MyStruct p)
  {
    p.Field = 42;
  }
  static void ChangeFieldOfMyClass_ByRef(ref MyClass p)
  {
    p.Field = 42;
  }
  static void ChangeFieldOfMyStruct_ByRef(ref MyStruct p)
  {
    p.Field = 42;
  }

  static void AssignToParameterMyClass_ByVal(MyClass p)
  {
    p = new MyClass { Field = 42, };
  }
  static void AssignToParameterMyStruct_ByVal(MyStruct p)
  {
    p = new MyStruct { Field = 42, };
  }
  static void AssignToParameterMyClass_ByRef(ref MyClass p)
  {
    p = new MyClass { Field = 42, };
  }
  static void AssignToParameterMyStruct_ByRef(ref MyStruct p)
  {
    p = new MyStruct { Field = 42, };
  }
}

The "value" of a class is a "reference" to a location of that instance of the class (object).

The "value" of a struct is all the struct instance fields taken togethed.

When something is passed "ByVal", the value is copied and handed to the method. For a class, that only involves copying the reference. We now have two distinct references to the same object on the heap. For a struct, copying the value means copying all the instance fields that this struct has.

When something is passed "ByRef", the method gets the very same value as the caller has. Nothing is copied. Same place in memory. For classes, there is one reference to the object. For structs, there is one place in memory where all the instance fields are kept.

(In the above, "class" could be a class type, interface type, delegate type or array type, and "struct" could be a struct type or enum type.)

C# also has pointers, in unsafe context, for example MyStruct* is the pointer type corresponding to MyStruct. Pointers (unlike "references" for class types) allow "arithmetics" like incrementing a pointer, or adding a value to a pointer. And you can cast pointers, for example from MyStruct* to byte*.

But pointers are rarely used. Avoid pointers like this: Use arrays (MyStruct[]) or generic collection types (e.g. List<MyStruct>) instead. If you want to re-interprete the data of say a MyStruct value into a list of four byte values (why?), make a method that does just this.



来源:https://stackoverflow.com/questions/21143521/my-implementation-of-swapt-does-not-work

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!