代码之家  ›  专栏  ›  技术社区  ›  andy

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

  •  2
  • andy  · 技术社区  · 15 年前

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

    我想向一个方法发送一组值,在这个方法中,我想根据一个实体的属性来测试这个值。属性的类型将始终与值的类型相同。

    我还想测试值是空值还是默认值,这显然取决于值类型是引用类型还是值类型。

    现在,如果发送到方法的所有值都是相同的类型,那么我可以使用泛型很容易地做到这一点,如下所示:

    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
                    }
                }
            }
        }
    

    我的问题是,如果我的集合包含不同类型的值,我将如何执行相同的函数。

    我主要关心的是成功地标识空值或默认值,并成功地标识传入的每个值是否等于同一类型的其他值。

    我可以通过简单地传递类型对象来实现这一点吗? 我也不能真正使用均等比较器,因为我不能使用泛型,因为我正在传入 未知数量的不同类型 .

    有解决办法吗?

    谢谢

    更新

    好的,四处搜索,我可以使用下面的代码在我的场景中成功地测试空值/默认值吗(取自 this SO answer ):

    object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
    

    我想这可能行得通。

    现在,我如何在不知道同一类型值的情况下成功地比较同一类型的两个值呢? 成功和可靠?

    3 回复  |  直到 15 年前
        1
  •  1
  •   Alex Yakunin    15 年前

    Object.Equals(object left, object right) 静态方法,它内部依赖于 Equals(object) 在提供的某个参数上可用的实现。你为什么不使用它?

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

    1. 必需:重写 等于(对象) GetHashCode() 方法
    2. 可选:机具 IEquatable<T> 对于你的类型(这就是 EqualityComparer.Default 依赖)
    3. 可选:机具==和!=算符

    如你所见,如果你要依靠 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
          }
        }
      }
    }
    
        2
  •  1
  •   Adam Robinson    15 年前

    简短的回答是“是的”,但较长的回答是,这是可能的,但需要你付出不小的努力和一些假设,才能使它发挥作用。当您的值在强类型代码中进行比较时被认为是“相等”的,但没有引用相等时,您的问题就会真正出现。你最大的冒犯者将是价值类型,如盒装 int 具有价值 1 不会与另一个装箱的相等 int 具有相同的值。

    考虑到这一点,你必须走上使用诸如 IComparable 接口。如果你的类型 总是 具体来说,这就足够了。如果您的任何一个值实现 可计算的 然后可以强制转换到该接口并与其他实例进行比较以确定相等性( ==0 )如果两者都不实现,那么您可能必须依赖于引用相等。对于引用类型,除非存在自定义比较逻辑(重载 == 例如,类型上的运算符)。

    请记住,类型必须完全匹配。换句话说,一个 int 和一个 short 不一定要这样比较,也不一定 int 和A double .

    您还可以沿着使用反射动态调用 Default 在运行时由提供的 Type 变量,但如果我不必为性能和编译时安全(或缺少安全性)而这样做的话,我不想这样做。

        3
  •  1
  •   Community leo1    7 年前

    您需要测试的类型列表是预先确定的列表吗?如果是,您可以使用 Visitor Pattern (即使没有,因为我们有仿制药)。在接受接口的实体(可以使用分部类)上创建方法。然后,类在该接口上调用一个传递自身的方法。接口方法可以是泛型的,也可以为要测试的每个类型创建重载。

    否则电池会死掉。


    点击“保存”15秒后,机器进入休眠状态。

    经过思考,访问者模式可能无法解决您的特定问题。我以为你在尝试比较实体,但你似乎在测试值(所以很可能是整数和字符串)。

    但是为了完成任务,并且因为一旦你意识到访客模式的作用,它就很酷了,这里有一个解释。

    访问者模式允许您处理多个类型,而无需了解如何强制转换为特定类型(使用该类型将类型与项分离)。它通过两个接口工作——访问者和接受者:

    interface IAcceptor
    {
      void Accept(IVisitor visitor);
    }
    
    interface IVisitor
    {
      void Visit(Type1 type1);
      void Visit(Type2 type2);
      .. etc ..
    }
    

    您可以选择在这里使用一个通用方法:

    interface IVisitor
    {
      void Visit<T>(T instance);
    }
    

    接受方法的基本实现是:

      void Accept(IVisitor visitor)
      {
        visitor.Visit(this);
      }
    

    因为实现accept()的类型知道它是什么类型,所以使用了正确的重载(或泛型类型)。您可以使用反射和查找表(或select语句)来实现相同的功能,但这要干净得多。另外,您不必在不同的实现之间重复查找——不同的类可以实现IVisitor来创建特定于类型的功能。

    访客模式是执行“双重调度”的一种方式。答案 this question 这是另一种方式,你可以把它变形成适合你具体情况的东西。

    基本上,一个长篇大论的回答你的问题,对不起。:)这个问题引起了我的兴趣,但是——比如,您如何知道应该测试实体的哪些属性?