比较不同类型的装箱值
Comparing boxed values of different types
在C#中,值类型可以装箱,这会导致某些比较问题,特别是针对不同类型。示例:2m == 2L
returns true
,但 (object)2m == (object)2L
returns false
。我的问题是:是否可以编写一个比较方法,在示例情况下获取两个对象(盒装值)参数和 returns true
?它必须适用于任何值类型组合,并且在值未装箱时具有与 ==
运算符相同的行为。谢谢!
我建议使用 dynamic
完成该任务。
object o1 = 2m;
object o2 = 2L;
if ((dynamic)o1 == (dynamic)o2) { Console.WriteLine("Works like charm"); }
但是,我并不完全了解 dynamic
关键字的所有含义,所以请注意!
我认为使用动态是解决这个问题的最佳方法,其他解决方案可能是这样的(Marshall 总是用于转换为更大的类型)
private static bool compareObj(object obj1, object obj2)
{
bool flag = true;
try
{
object result = Convert.ChangeType(obj1, obj2.GetType());
object result2 = Convert.ChangeType(obj2, obj1.GetType());
var first = Marshal.SizeOf(obj1.GetType());
var second = Marshal.SizeOf(obj2.GetType());
if (first > second)
{
flag = obj1.Equals(result2);
}
else
{
flag = obj2.Equals(result);
}
}
catch (InvalidCastException ex)
{
flag = false;
}
return flag;
}
由于 dynamic
的公认解决方案在找不到合适的 ==
运算符时可能会抛出异常(请参阅我关于比较 3UL
和 3L
值的评论), 我已经实现了另一种检查相等性的方法。
如果两个装箱的值属于同一类型,下面的代码将调用 Equals
方法,或者尝试通过取消装箱将这些值比较为通用类型。 float
和double
类型有特殊处理,其余整数类型通过转换为decimal
.
进行比较
此方法比公认的解决方案慢一点,但它处理的情况更多,并且从内存分配的角度来看表现更好:
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------------- |----------:|---------:|---------:|-------:|------:|------:|----------:|
| AreEqualDynamic | 83.31 ns | 1.447 ns | 1.354 ns | 0.0172 | - | - | 72 B |
| AreEqualConvert | 112.44 ns | 1.156 ns | 0.902 ns | - | - | - | - |
AreEqualConvert
方法实现:
public bool AreEqualConvert(object o1, object o2)
{
if (ReferenceEquals(o1, o2))
{
return true;
}
if (o1 is null || o2 is null)
{
return false;
}
if (o1.GetType() == o2.GetType())
{
return o1.Equals(o2);
}
switch (o1)
{
case float f1:
switch (o2)
{
case double d2:
return f1 == d2;
case IConvertible c2:
return f1 == c2.ToSingle(null);
default:
return false;
}
case double d1:
return o2 is IConvertible conv2
? d1 == conv2.ToDouble(null)
: false;
case IConvertible c1:
switch (o2)
{
case float f2:
return c1.ToSingle(null) == f2;
case double d2:
return c1.ToDouble(null) == d2;
case IConvertible c2:
return c1.ToDecimal(null) == c2.ToDecimal(null);
default:
return false;
}
default:
return false;
}
}
在C#中,值类型可以装箱,这会导致某些比较问题,特别是针对不同类型。示例:2m == 2L
returns true
,但 (object)2m == (object)2L
returns false
。我的问题是:是否可以编写一个比较方法,在示例情况下获取两个对象(盒装值)参数和 returns true
?它必须适用于任何值类型组合,并且在值未装箱时具有与 ==
运算符相同的行为。谢谢!
我建议使用 dynamic
完成该任务。
object o1 = 2m;
object o2 = 2L;
if ((dynamic)o1 == (dynamic)o2) { Console.WriteLine("Works like charm"); }
但是,我并不完全了解 dynamic
关键字的所有含义,所以请注意!
我认为使用动态是解决这个问题的最佳方法,其他解决方案可能是这样的(Marshall 总是用于转换为更大的类型)
private static bool compareObj(object obj1, object obj2)
{
bool flag = true;
try
{
object result = Convert.ChangeType(obj1, obj2.GetType());
object result2 = Convert.ChangeType(obj2, obj1.GetType());
var first = Marshal.SizeOf(obj1.GetType());
var second = Marshal.SizeOf(obj2.GetType());
if (first > second)
{
flag = obj1.Equals(result2);
}
else
{
flag = obj2.Equals(result);
}
}
catch (InvalidCastException ex)
{
flag = false;
}
return flag;
}
由于 dynamic
的公认解决方案在找不到合适的 ==
运算符时可能会抛出异常(请参阅我关于比较 3UL
和 3L
值的评论), 我已经实现了另一种检查相等性的方法。
如果两个装箱的值属于同一类型,下面的代码将调用 Equals
方法,或者尝试通过取消装箱将这些值比较为通用类型。 float
和double
类型有特殊处理,其余整数类型通过转换为decimal
.
此方法比公认的解决方案慢一点,但它处理的情况更多,并且从内存分配的角度来看表现更好:
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------------- |----------:|---------:|---------:|-------:|------:|------:|----------:|
| AreEqualDynamic | 83.31 ns | 1.447 ns | 1.354 ns | 0.0172 | - | - | 72 B |
| AreEqualConvert | 112.44 ns | 1.156 ns | 0.902 ns | - | - | - | - |
AreEqualConvert
方法实现:
public bool AreEqualConvert(object o1, object o2)
{
if (ReferenceEquals(o1, o2))
{
return true;
}
if (o1 is null || o2 is null)
{
return false;
}
if (o1.GetType() == o2.GetType())
{
return o1.Equals(o2);
}
switch (o1)
{
case float f1:
switch (o2)
{
case double d2:
return f1 == d2;
case IConvertible c2:
return f1 == c2.ToSingle(null);
default:
return false;
}
case double d1:
return o2 is IConvertible conv2
? d1 == conv2.ToDouble(null)
: false;
case IConvertible c1:
switch (o2)
{
case float f2:
return c1.ToSingle(null) == f2;
case double d2:
return c1.ToDouble(null) == d2;
case IConvertible c2:
return c1.ToDecimal(null) == c2.ToDecimal(null);
default:
return false;
}
default:
return false;
}
}