88

我希望能够在不使用 C# 中的临时变量的情况下交换两个变量。这可以做到吗?

decimal startAngle = Convert.ToDecimal(159.9);
decimal stopAngle = Convert.ToDecimal(355.87);

// Swap each:
//   startAngle becomes: 355.87
//   stopAngle becomes: 159.9
4

29 回答 29

229

C# 7 引入了元组,可以在没有临时变量的情况下交换两个变量:

int a = 10;
int b = 2;
(a, b) = (b, a);

这分配baab

于 2016-08-28T11:29:46.530 回答
226

交换两个变量的正确方法是:

decimal tempDecimal = startAngle;
startAngle = stopAngle;
stopAngle = tempDecimal;

换句话说,使用临时变量。

你有它。没有聪明的技巧,没有你的代码维护者在未来几十年里诅咒你,没有进入The Daily WTF 的条目,也没有花费太多时间试图找出你为什么需要它在一个操作中,因为在最低级别,即使是最复杂的语言特征是一系列简单的操作。

只是一个非常简单,可读,易于理解的t = a; a = b; b = t;解决方案。

在我看来,尝试使用技巧的开发人员,例如,“不使用临时变量交换变量”或“Duff 的设备”只是试图展示他们有多聪明(并且失败得很惨)。

我把他们比作那些仅仅为了在聚会上看起来更有趣(而不是扩大你的视野)而阅读高雅书籍的人。

您添加和减去的解决方案或基于 XOR 的解决方案的可读性较差,并且很可能比简单的“临时变量”解决方案(算术/布尔运算而不是装配级别的简单移动)更慢。

通过编写高质量的可读代码为自己和他人提供服务。

那是我的吐槽。感谢收听 :-)

顺便说一句,我很清楚这并不能回答你的具体问题(我会为此道歉),但是有很多关于 SO 的先例,人们问如何做某事,正确的答案是“不要做”。

于 2009-04-30T02:05:39.773 回答
133

首先,在 C# 语言中不使用临时变量进行交换是一个非常糟糕的主意

但为了回答,您可以使用以下代码:

startAngle = startAngle + stopAngle;
stopAngle = startAngle - stopAngle;
startAngle = startAngle - stopAngle;

但是,如果两个数字相差很大,则四舍五入可能会出现问题。这是由于浮点数的性质。

如果要隐藏临时变量,可以使用实用方法:

public static class Foo {

    public static void Swap<T> (ref T lhs, ref T rhs) {
        T temp = lhs;
        lhs = rhs;
        rhs = temp;
    }
}
于 2009-04-29T23:20:58.437 回答
74

是的,使用以下代码:

stopAngle = Convert.ToDecimal(159.9);
startAngle = Convert.ToDecimal(355.87);

对于任意值,问题更难。:-)

于 2009-04-29T23:31:00.643 回答
44
int a = 4, b = 6;
a ^= b ^= a ^= b;

适用于所有类型,包括字符串和浮点数。

于 2009-12-29T07:34:49.230 回答
23

BenAlabaster 展示了一种进行变量切换的实用方法,但不需要 try-catch 子句。这段代码就够了。

static void Swap<T>(ref T x, ref T y)
{
     T t = y;
     y = x;
     x = t;
}

用法和他展示的一样:

float startAngle = 159.9F
float stopAngle = 355.87F
Swap(ref startAngle, ref stopAngle);

您还可以使用扩展方法:

static class SwapExtension
{
    public static T Swap<T>(this T x, ref T y)
    {
        T t = y;
        y = x;
        return t;
    }
}

像这样使用它:

float startAngle = 159.9F;
float stopAngle = 355.87F;
startAngle = startAngle.Swap(ref stopAngle);

两种方式都在方法中使用临时变量,但您不需要在进行交换时使用临时变量。

于 2013-03-05T18:17:23.437 回答
18

带有详细示例的二进制 XOR 交换:

异或真值表

a b a^b
0 0  0
0 1  1
1 0  1
1 1  0

输入:

a = 4;
b = 6;

第 1 步a = a ^ b

a  : 0100
b  : 0110
a^b: 0010 = 2 = a

第 2 步b = a ^ b

a  : 0010
b  : 0110
a^b: 0100 = 4 = b

第 3 步a = a ^ b

a  : 0010
b  : 0100
a^b: 0110 = 6 = a

输出:

a = 6;
b = 4;
于 2013-10-27T13:53:44.600 回答
14

不在 C# 中。在本机代码中,您可能能够使用三异或交换技巧,但不能在高级类型安全语言中使用。(无论如何,我听说 XOR 技巧实际上最终比在许多常见的 CPU 架构中使用临时变量要慢。)

你应该只使用一个临时变量。没有理由不能使用它;这不像是供应有限。

于 2009-04-29T23:13:58.547 回答
13

为了未来的学习者和人类,我将这个更正提交给当前选择的答案。

如果你想避免使用临时变量,只有两个明智的选择,首先考虑性能,然后考虑可读性。

  • Swap在泛型方法中使用临时变量。(绝对最佳性能,在 inline temp 变量旁边)
  • 使用Interlocked.Exchange. (在我的机器上慢 5.9 倍,但如果多个线程同时交换这些变量,这是你唯一的选择。)

永远不应该做的事情:

  • 永远不要使用浮点运算。(缓慢、舍入和溢出错误,难以理解)
  • 永远不要使用非原始算术。(缓慢,溢出错误,难以理解)Decimal不是 CPU 原语,导致的代码比您意识到的要多得多。
  • 切勿使用算术周期。或位黑客。(缓慢,难以理解)这是编译器的工作。它可以针对许多不同的平台进行优化。

因为每个人都喜欢硬数字,所以这里有一个程序可以比较您的选择。从 Visual Studio 外部以发布模式运行它,以便Swap内联。我的机器上的结果(Windows 7 64 位 i5-3470):

Inline:      00:00:00.7351931
Call:        00:00:00.7483503
Interlocked: 00:00:04.4076651

代码:

class Program
{
    static void Swap<T>(ref T obj1, ref T obj2)
    {
        var temp = obj1;
        obj1 = obj2;
        obj2 = temp;
    }

    static void Main(string[] args)
    {
        var a = new object();
        var b = new object();

        var s = new Stopwatch();

        Swap(ref a, ref b); // JIT the swap method outside the stopwatch

        s.Restart();
        for (var i = 0; i < 500000000; i++)
        {
            var temp = a;
            a = b;
            b = temp;
        }
        s.Stop();
        Console.WriteLine("Inline temp: " + s.Elapsed);


        s.Restart();
        for (var i = 0; i < 500000000; i++)
        {
            Swap(ref a, ref b);
        }
        s.Stop();
        Console.WriteLine("Call:        " + s.Elapsed);

        s.Restart();
        for (var i = 0; i < 500000000; i++)
        {
            b = Interlocked.Exchange(ref a, b);
        }
        s.Stop();
        Console.WriteLine("Interlocked: " + s.Elapsed);

        Console.ReadKey();
    }
}
于 2014-07-18T16:28:57.480 回答
12

在 C# 7 中:

(startAngle, stopAngle) = (stopAngle, startAngle);
于 2017-09-29T13:39:25.020 回答
8

<已弃用>

您可以使用基本数学在 3 行中完成 - 在我的示例中,我使用了乘法,但简单的加法也可以。

float startAngle = 159.9F;
float stopAngle = 355.87F;

startAngle = startAngle * stopAngle;
stopAngle = startAngle / stopAngle;
startAngle = startAngle / stopAngle;

编辑:如评论中所述,如果 y = 0 这将不起作用,因为它会产生除以零的错误,这是我没有考虑过的。因此,另外提出的 +/- 解决方案将是最好的方法。

</弃用>


为了让我的代码立即被理解,我更有可能做这样的事情。[总是想想那些不得不维护你的代码的可怜人]:

static bool Swap<T>(ref T x, ref T y)
{
    try
    {
        T t = y;
        y = x;
        x = t;
        return true;
    }
    catch
    {
        return false;
    }
}

然后你可以在一行代码中做到这一点:

float startAngle = 159.9F
float stopAngle = 355.87F
Swap<float>(ref startAngle, ref stopAngle);

或者...

MyObject obj1 = new MyObject("object1");
MyObject obj2 = new MyObject("object2");
Swap<MyObject>(ref obj1, ref obj2);

像晚餐一样完成...您现在可以传入任何类型的对象并切换它们...

于 2009-04-29T23:32:01.780 回答
7

为了完整起见,这里是二进制 XOR 交换:

int x = 42;
int y = 51236;
x ^= y;
y ^= x;
x ^= y;

这适用于所有原子对象/引用,因为它直接处理字节,但可能需要不安全的上下文来处理小数,或者,如果你感觉真的很扭曲,指针。在某些情况下,它也可能比临时变量慢。

于 2009-05-01T09:42:37.077 回答
6

如果您可以从 using 更改decimaldouble您可以使用Interlocked该类。据推测,这将是交换变量性能的好方法。也比 XOR 更具可读性。

var startAngle = 159.9d;
var stopAngle = 355.87d;
stopAngle = Interlocked.Exchange(ref startAngle, stopAngle);

Msdn: Interlocked.Exchange Method (Double, Double)

于 2013-07-30T07:39:45.383 回答
6

使用 C# 7,您可以使用元组解构在一行中实现所需的交换,并且很清楚发生了什么。

decimal startAngle = Convert.ToDecimal(159.9);
decimal stopAngle = Convert.ToDecimal(355.87);

(startAngle, stopAngle) = (stopAngle, startAngle);
于 2017-07-22T01:12:41.430 回答
5
a = a + b
b = a - b
a = a - b

于 2009-07-05T17:40:44.277 回答
4

小心你的环境!

例如,这似乎不适用于 ECMAscript

y ^= x ^= y ^= x;

但这确实

x ^= y ^= x; y ^= x;

我的建议?尽可能少地假设。

于 2010-10-08T03:21:26.077 回答
4

在一行中交换 2 个数字的简单方法:

a=(a+b)-(b=a);

例如:a=1, b=2

第 1 步:a=(1+2) - (b=1)

第 2 步:a=3-1

=> a=2 和 b=1


有效的方法是使用:

C 编程: (x ^= y), (y ^= x), (x ^= y);

爪哇: x = x ^ y ^ (y = x);

Python:x, y = y, x

注意:人们最常犯的错误: //Swap using bitwise XOR (Wrong Solution in C/C++)

x ^= y ^= x ^= y; 

资料来源:GeeksforGeek

于 2016-05-05T20:58:29.863 回答
3

对于二进制类型,您可以使用这个时髦的技巧:

a %= b %= a %= b;

只要 a 和 b 不是完全相同的变量(例如,同一内存的别名),它就可以工作。

于 2009-04-29T23:25:57.020 回答
3

我希望这可能会有所帮助...

using System;

public class Program
{
    public static void Main()
    {
        int a = 1234;
        int b = 4321;

        Console.WriteLine("Before: a {0} and b {1}", a, b);

        b = b - a;
        a = a + b;
        b = a - b;

        Console.WriteLine("After: a {0} and b {1}", a, b);
    }
}
于 2016-03-15T23:49:20.827 回答
2
startAngle = (startAngle + stopAngle) - (stopAngle = startAngle);
于 2015-02-14T09:30:18.783 回答
2

我们可以通过一个简单的技巧来做到这一点

a = 20;
b = 30;
a = a+b; // add both the number now a has value 50
b = a-b; // here we are extracting one number from the sum by sub
a = a-b; // the number so obtained in above help us to fetch the alternate number from sum
System.out.print("swapped numbers are a = "+ a+"b = "+ b);
于 2017-06-30T17:37:17.387 回答
1

如果要交换 2 个字符串变量:

a = (a+b).Substring((b=a).Length);

相应的辅助方法:

public static class Foo {
    public static void SwapString (ref string a, ref string b) {
       a = (a+b).Substring((b=a).Length);
    }
}

用法将是:

string a="Test 1";
string b="Test 2";
Foo.SwapString(a, b);
于 2017-08-30T14:07:17.817 回答
1

有时我希望可以在 C# 中使用 MSIL 内联编写函数,类似于在 C 中编写内联汇编程序。

作为记录,我曾经为 C# 编写了一个帮助程序库,其中包含各种函数,这些函数无法用 C# 编写,但可以用 MSIL 编写(例如,非从零开始的数组)。我有这个功能:

.method public hidebysig static void Swap<T> (
        !!T& a,
        !!T& b
    ) cil managed 
{
    .maxstack 4

    ldarg.1      // push a& reference
    ldarg.2      // push b& reference
    ldobj !!T    // pop b&, push b
    ldarg.2      // push b& reference
    ldarg.1      // push a& reference
    ldobj !!T    // pop a&, push a
    stobj !!T    // store a in b&
    stobj !!T    // store b in a&
    ret
}

而且不需要当地人。当然,这只是我的愚蠢...

于 2021-01-30T23:33:17.430 回答
0

这里有另一种方法:

decimal a = 159.9m;
decimal b = 355.87m;

a = b + (b = a) - b;
于 2017-03-24T12:46:34.417 回答
0

这是交换两个变量的一些不同过程

//process one
a=b+a;
b=a-b;
a=a-b;
printf("a= %d  b=  %d",a,b);

//process two
a=5;
b=10;
a=a+b-(b=a);
printf("\na= %d  b=  %d",a,b);

//process three
a=5;
b=10;
a=a^b;
b=a^b;
a=b^a;
printf("\na= %d  b=  %d",a,b);

//process four
a=5;
b=10;
a=b-~a-1;
b=a+~b+1;
a=a+~b+1;
printf("\na= %d  b=  %d",a,b);
于 2017-08-11T19:16:40.603 回答
0

只为答题

        var a = 10;
        var b = 20;

        var c = new int[] { a, b };

        a = c[1];
        b = c[0];
于 2021-09-11T14:42:02.373 回答
-1
var a = 15;
var b = -214;
a = b | !(b = a);

这很好用。

于 2013-03-30T13:52:05.427 回答
-4

用于交换两个变量的非常简单的代码:

static void Main(string[] args)
{
    Console.WriteLine("Prof.Owais ahmed");
    Console.WriteLine("Swapping two variables");

    Console.WriteLine("Enter your first number ");
    int x = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("Enter your first number ");
    int y = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("your vlaue of x is="+x+"\nyour value of y is="+y);

    int z = x;
    x = y;
    y = z;

    Console.WriteLine("after Swapping value of x is="+x+"/nyour value of y is="+y);
    Console.ReadLine();
}
于 2015-08-09T08:07:37.447 回答
-5

你可以试试下面的代码。它比其他代码要好得多。

a = a + b;
b = a - b;
a = a - b;
于 2016-06-23T08:48:23.727 回答