使用未知数量的未知类型 - .NET

时间:2009-06-26 01:13:24

标签: c# .net generics types

嘿伙计们,我已经把我需要的一些复杂性移到了我需要知道的核心。

我想将一组值发送到一个方法,在该方法中我想测试一个实体的属性。该属性将始终与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;

我认为这可行。

现在,如何成功可靠地比较相同类型的两个值,而不知道其类型

3 个答案:

答案 0 :(得分:1)

简短的回答是“是”,但更长的答案是,这是可能的,但是为了使其发挥作用,您需要花费大量的精力和一些假设。当您在强类型代码中进行比较时,您的问题将被视为“相等”,但是没有引用相等性,那么您的问题确实会出现。您最大的违规者将是值类型,因为值为int的盒装1将不会与具有相同值的另一个盒装int具有引用相等性。

鉴于此,您必须继续使用IComparable界面之类的东西。如果您的类型始终特别匹配,那么这可能就足够了。如果您的任何一个值实现IComparable,那么您可以转换为该接口并与另一个实例进行比较以确定相等性(==0)。如果两者都没有实现它,那么你可能不得不依赖于引用相等。对于引用类型,除非存在自定义比较逻辑(例如,类型上的重载==运算符),否则这将起作用。

请记住,类型必须完全匹配。换句话说,intshort不一定会像这样比较,intdouble也不会。

您还可以沿着使用反射的路径动态调用由运行时由提供的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)实现。你为什么要避免使用它?

实施平等成员的规则几乎如下:

  1. 必填:覆盖Equals(object)GetHashCode()方法
  2. 可选:为您的类型实施IEquatable<T>(这是EqualityComparer.Default所依赖的)
  3. 可选:实施==和!=运算符
  4. 如您所见,如果您依赖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
          }
        }
      }
    }