所以我在下面写下应该用于引用和结构类型的快速代码,但是第一个代码根本没有做任何有用的事情,其次对于List<int>
没用。任何人都可以在下面的代码中指出错误和设计问题吗?
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;
}
}
为什么我要这样做:我已经阅读了一些关于为什么C#中没有交换的问题。常见的声明是Swap没用,你应该只使用temp obj来做。但是,我仍然不相信。我现在正在编写代码,它使用了大量的交换。将相同的代码片段传播到各处看起来真的很不舒服。在多个方法中使用相同的行仍然很烦人,但对我的项目来说要好得多。
更新
基于所有输入,我意识到我对C#知之甚少并且已经产生了正确而简洁的解决方案:
public static void Swap<T>(ref T o1, ref T o2)
{
var temp = o1;
o1 = o2;
o2 = temp;
}
两个想法:
检查o1和o2在性能方面的参考相等是否有用?我想这不是一个非常糟糕的主意,但是我应该使用object.ReferenceEquals()吗?如果是,如果T是原始类型,它是否会调用拳击?
因为使用了ref关键字,所以我们无法传递List的元素。我写了一个扩展方法来交换List元素:
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;
}
答案 0 :(得分:0)
第一个版本,带有引用类型(约束where T : class
),显然缺少两个参数的ref
修饰符。当参数未通过“ref”传递时,为o1
和o2
分配新引用对调用者完全没有任何改变。
因此,在这种情况下,对于引用类型和值类型,确实没有理由使用单独的方法(顺便说一下,两个重载不允许具有相同的签名,非法C#!)。
另外,目前还不清楚为什么你不想仅仅因为CompareTo
给出0
而进行交换。交换引用仍然很重要;它可能仍然是不同的实例。
您可能会混淆两个不相关的概念:(1)引用类型(class
)和值类型(struct
)。 (2)ByRef参数(ref
或out
关键字)和值参数(既不存在ref
也不存在out
)。
编辑: 您可以通过此示例测试您的理解:
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, };
}
}
类的“值”是对该类(对象)实例的位置的“引用”。
结构的“值”是所有采用的结构实例字段。
当某些内容传递“ByVal”时,值被复制并传递给该方法。对于一个类,只涉及复制引用。我们现在对堆上的同一个对象有两个不同的引用。对于结构,复制值意味着复制此结构具有的所有实例字段。
当某些内容传递“ByRef”时,该方法获得与调用者具有的值相同的值。什么都没有复制。记忆中的同一个地方。对于类,对该对象有一个引用。对于结构体,内存中有一个位置,其中保留了所有实例字段。
(在上文中,“class”可以是class
类型,interface
类型,delegate
类型或数组类型,“struct”可以是struct
键入或enum
类型。)
C#在<{1}}上下文中也有指针,例如unsafe
是与MyStruct*
对应的指针类型。指针(与MyStruct
类型的“引用”不同)允许“算术”,例如递增指针或向指针添加值。您可以投射指针,例如从class
到MyStruct*
。
但很少使用指针。避免这样的指针:使用数组(byte*
)或泛型集合类型(例如MyStruct[]
)。如果您想将List<MyStruct>
值的数据重新解释为四个MyStruct
值列表(为什么?),请创建一个方法来实现此目的。