我想将一组值发送到一个方法,在该方法中我想测试一个实体的属性。该属性将始终与Value类型相同。
我还想测试值是否为null或默认值,显然取决于值类型是引用类型还是值类型。
现在,如果发送给方法的所有值都属于同一类型,那么我可以使用泛型来完成此操作,非常容易,如下所示:
public static void testGenerics<TValueType>(List<TValueType> Values) {
//test null/default
foreach (TValueType v in Values) {
if (EqualityComparer<TValueType>.Default.Equals(v, default(TValueType))) {
//value is null or default for its type
} else {
//comapre against another value of the same Type
if (EqualityComparer<TValueType>.Default.Equals(v, SomeOtherValueOfTValueType)) {
//value equals
} else {
//value doesn't equal
}
}
}
}
我的问题是,如果我的Collection包含不同类型的值,我将如何执行相同的功能。
我主要关注的是成功识别空值或默认值,并成功识别传入的每个值是否等于同一类型的其他值。
我可以通过简单地传递类型对象来实现这一点吗?我也不能真正使用EqualityComparers,因为我不能使用泛型,因为我传入未知数字不同类型。
有解决方案吗?
感谢
更新
好的,搜索一下,我可以使用以下代码在我的场景中成功测试null / default(取自this SO answer):
object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
我认为这可行。
现在,如何成功可靠地比较相同类型的两个值,而不知道其类型?
答案 0 :(得分:1)
简短的回答是“是”,但更长的答案是,这是可能的,但是为了使其发挥作用,您需要花费大量的精力和一些假设。当您在强类型代码中进行比较时,您的问题将被视为“相等”,但是没有引用相等性,那么您的问题确实会出现。您最大的违规者将是值类型,因为值为int
的盒装1
将不会与具有相同值的另一个盒装int
具有引用相等性。
鉴于此,您必须继续使用IComparable
界面之类的东西。如果您的类型始终特别匹配,那么这可能就足够了。如果您的任何一个值实现IComparable
,那么您可以转换为该接口并与另一个实例进行比较以确定相等性(==0
)。如果两者都没有实现它,那么你可能不得不依赖于引用相等。对于引用类型,除非存在自定义比较逻辑(例如,类型上的重载==
运算符),否则这将起作用。
请记住,类型必须完全匹配。换句话说,int
和short
不一定会像这样比较,int
和double
也不会。
您还可以沿着使用反射的路径动态调用由运行时由提供的Default
变量确定的泛型类型上的Type
属性,但我不想这样做,如果我没有必要考虑性能和编译时的安全性(或缺乏安全性)。
答案 1 :(得分:1)
您需要测试预定列表的类型列表吗?如果是这样,你可以使用Visitor Pattern(也许即使没有,因为我们有Generics)。在您的实体上创建一个方法(可以使用部分类来完成),该方法接受一个接口。然后,您的类调用该接口上传递自身的方法。接口方法可以是通用的,也可以为要测试的每种类型创建重载。
电池即将死,否则就会举例。
点击“保存”后15秒,机器进入休眠状态。
在考虑之后,访客模式可能无法解决您的具体问题。我以为你试图比较实体,但看起来你正在测试值(所以可能是int和字符串)。
但是为了完成,并且因为一旦你意识到它的作用,访客模式就会很酷,这里有一个解释。
访问者模式允许您处理多种类型,而无需弄清楚如何转换为特定类型(使用该类型将类型与项目分离)。它的工作原理是有两个接口 - 访问者和接受者:
interface IAcceptor
{
void Accept(IVisitor visitor);
}
interface IVisitor
{
void Visit(Type1 type1);
void Visit(Type2 type2);
.. etc ..
}
您可以选择在那里使用通用方法:
interface IVisitor
{
void Visit<T>(T instance);
}
accept方法的基本实现是:
void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
因为实现Accept()的类型知道它是什么类型,所以使用正确的重载(或泛型类型)。您可以使用反射和查找表(或select语句)实现相同的功能,但这更加清晰。此外,您不必在不同的实现之间复制查找 - 各种类可以实现IVisitor来创建特定于类型的功能。
访客模式是进行“双重调度”的一种方式。 this question的答案是另一种方式,您可以将其变形为适合您特定情况的内容。
基本上,一个啰嗦的不回答你的问题,对不起。 :)问题引起了我的兴趣 - 比如你怎么知道你应该测试的实体上有什么属性?
答案 2 :(得分:1)
有Object.Equals(object left, object right)
静态方法,它内部依赖于提供的参数之一的Equals(object)
实现。你为什么要避免使用它?
实施平等成员的规则几乎如下:
Equals(object)
和GetHashCode()
方法IEquatable<T>
(这是EqualityComparer.Default
所依赖的)如您所见,如果您依赖object.Equals(object left, object right)
,这将是依赖强烈要求的等同实现模式
此外,它将是最快的选择,因为它仅依赖于虚拟方法。否则你无论如何都会有一些反思。
public static void TestGenerics(IList values) {
foreach (object v in values) {
if (ReferenceEquals(null,v)) {
// v is null reference
}
else {
var type = v.GetType();
if (type.IsValueType && Equals(v, Activator.CreateInstance(type))) {
// v is default value of its value type
}
else {
// v is non-null value of some reference type
}
}
}
}