Поменять местами две переменные без использования ref/out в С#

Можно ли поменять местами две переменные без использования ключевого слова ref/out в С# (т.е. с помощью небезопасного)?

например,

        swap(ref x,ref y);//It is possbile to by passing the reference

Но есть ли другой способ сделать то же самое. В функции подкачки вы можете использовать временную переменную. Но как поменять местами переменные без использования ключевых слов ref/out в С#?


person Aditya Singh    schedule 08.10.2010    source источник


Ответы (8)


(Очень!) Надуманный пример с использованием делегатов:

class Program
{
    static void FunkySwap<T>(T a, T b, Action<T> setA, Action<T> setB)
    {
        T tempA = a;
        setA(b);
        setB(tempA);
    }
    static void Main(string[] args)
    {
        string s1 = "big";
        string s2 = "apples";
        Console.WriteLine("BEFORE, s1: {0}, s2: {1}", s1, s2);
        FunkySwap(s1, s2, a => s1 = a, b => s2 = b);
        Console.WriteLine("AFTER,  s1: {0}, s2: {1}", s1, s2);
    }
}

Хотя приведенное выше довольно глупо, использование метода установки делегата может быть полезно в других ситуациях; Я использовал метод реализации отмены/возврата изменений свойств.

person snemarch    schedule 08.10.2010
comment
Это очень мило (ну, это довольно ужасно, но это хорошо как ответ), поскольку мы не можем передавать переменные без ref или подделывать ref с указателями или объектным включением, вместо этого вы передаете функцию! Хорошее латеральное мышление. - person Jon Hanna; 08.10.2010
comment
Несмотря на то, что он не похож ни на что, это дубликат ответа Майкла Шимминса. - person Ben Voigt; 08.10.2010
comment
@Бен Фойгт: как так? Его выделяет новую структуру, моя использует делегатов и изменяет исходные ссылки. - person snemarch; 08.10.2010
comment
Ах, вы правы, возврат структуры не так уж и похож. Однако использование замыканий точно такое же, как помещение значений, подлежащих обмену, в экземпляр класса, вызов функции для обмена членами и последующее чтение их из экземпляра класса, потому что именно это на самом деле делает код MSIL. - person Ben Voigt; 08.10.2010
comment
@Ben Voigt: ну, есть большая разница в сгенерированном коде MSIL, но вы правы, что есть значительные накладные расходы, которые не видны из кода C #. На каждом сайте вызова создаются два объекта Action, и FunkySwap() выполняет два вызова виртуальных методов. Решение Swapable имеет гораздо более короткий сайт вызова, выделяет один новый объект и загружает/сохраняет только простое поле в Swap() :) - person snemarch; 13.10.2010

Нет, невозможно воздействовать на переменные* в вызывающей программе без использования ref или out передачи по ссылке. Вы можете воздействовать на членов класса, но как узнать, какие из них вас просят поменять местами?

*(Вы можете воздействовать на экземпляры ссылочных типов, и изменения будут видны вызывающей стороне, но экземпляры не являются «переменными».)

person Ben Voigt    schedule 08.10.2010
comment
Ну, он спрашивает, можно ли использовать небезопасный, и, поскольку небезопасные блоки кода позволяют использовать указатели, неверно утверждать, что это можно сделать без ref или out - person Rune FS; 08.10.2010
comment
@Rune: вы просто создаете параметр передачи по ссылке, не используя ключевое слово ref. Это действительно соответствует букве вопроса, мне просто нужно задаться вопросом, соответствует ли это духу. - person Ben Voigt; 08.10.2010

Не совсем, если только ваша единственная проблема с ref не связана с неприязнью к самому слову.

Вы можете переключаться между указателями, например:

public unsafe void Swap(int* x, int* y)
{
    unsafe
    {//lets not use a temp, just to be different!
        *x ^= *y;
        *y ^= *x;
        *x ^= *y;
    }
}

Но на самом деле единственное практическое отличие состоит во всех ловушках указателей, от которых вас спасают ссылки, а также в том, что это должно быть небезопасно. Это в основном делает то же самое.

person Jon Hanna    schedule 08.10.2010

я пробовал это

class Program
{
    static void Main(string[] args)
    {
        int x = 3;
        int y = 6;
        Console.Write(string.Format("before swap x={0} y={1}", x, y));
        Swap(x, y);
        Console.Write(string.Format("after swap x={0} y={1}", x, y));
        Console.ReadKey();
    }

    static public unsafe void Swap(int a, int b)
    {
        int* ptrToA = &a;
        int* ptrToB = &b;
        int c = a;
        *ptrToB = c;
        *ptrToB = *ptrToA;
    }
}

И совершенно забыл, что ints передаются по значению, и я никак не могу взять указатель на что-то, что на самом деле КОПИИРОВАНО из вызывающего в стек вызываемого.

так что это не работает

Так что, похоже, вместо того, чтобы быть умнее, я просто потратил немного времени впустую, но все равно хочу поделиться этим с вами :)

person Daniel Mošmondor    schedule 08.10.2010

Вы можете передать значения в объект и вернуть экземпляр этого объекта с замененными значениями:

public struct Swapable
{
    public int A;
    public int B;
}

public Swapable Swap(Swapable swapable)
{
    return new Swapable()
    {
        A = swapable.B;
        B = swapable.A;
    };
}
person Michael Shimmins    schedule 08.10.2010

Я пробовал использовать unsafe, и он работает, см. код

namespace ConsoleApplication2
{
class myclass
{
    public unsafe void swap(int *x, int *y)
    {
        unsafe
        {
            int temp = 0;
            temp = *x;
            *x = *y;
            *y = temp;
            Console.WriteLine("Using Swap1");
            Console.WriteLine("x:"+*x);
            Console.WriteLine("y:" + *y);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        unsafe
        {
            int x = 10;
            int y = 20;
            int* t1 = &x;
            int* t2 = &y;
            myclass m1 = new myclass();
            m1.swap(t1,t2);
            Console.WriteLine("Main");
            Console.WriteLine("x: " + x);
            Console.WriteLine("y: " + y);
            Console.ReadLine();

        }
    }
}

}

person Aditya Singh    schedule 08.10.2010

здесь я передаю объект в качестве аргументов

class person1
            {
                public int x, y;
                public person1(int x,int y)
                    {
                    this.x = x;
                    this.y = y;

                    }
                public void changes(person1 p1)
                {
                   // int t;
                    this.x = x + y;  //x=30 x=10,y=20
                    this.y = x - y;  //y=30-20=10
                    this.x = x - y; //x=30-10=20
                }
    }
     static void Main(string[] args)
       {
             person1 p1 = new person1(10,20);
             p1.changes(p1);
             Console.WriteLine("swapp hoge kya ?x={0} and y={1}", p1.x, p1.y);
             Console.ReadKey();
      }
person Community    schedule 25.02.2017

С кортежами

int a = 4, b = 8;
(a, b) = (b, a);
person Zu1779    schedule 03.05.2018