【问题标题】:Working with an unknown number of unknown types - .NET使用未知数量的未知类型 - .NET
【发布时间】:2009-06-26 01:13:24
【问题描述】:

大家好,我已经将我需要了解的一些复杂性内容放到了我需要了解的核心内容中。

我想将一组值发送到一个方法,并在该方法中测试该值,例如,一个实体的属性。该属性将始终与值的类型相同。

我也想测试一下值是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;

我认为这可能有效。

现在,我如何在不知道它们的类型的情况下成功地比较两个相同类型的值

【问题讨论】:

    标签: c# .net generics types


    【解决方案1】:

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

    实现相等成员的规则大致如下:

    1. 必需:覆盖 Equals(object)GetHashCode() 方法
    2. 可选:为您的类型实现 IEquatable&lt;T&gt;(这是 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
          }
        }
      }
    }
    

    【讨论】:

    • 酷,谢谢亚历克斯。关于你们的 cmets,我不完全理解,但我认为我们走在正确的轨道上。您能否提供一些代码 sn-ps,向我展示如何实现您所说的内容?干杯
    • 注意:Activator.CreateInsttance 相当慢 - 我建议您缓存其调用的结果。
    【解决方案2】:

    简短的回答是“是”,但较长的回答是,这是可能的,但需要您付出不小的努力和一些假设才能使其发挥作用。当您的值在强类型代码中比较时被认为是“相等”但没有引用相等时,您的问题就真的出现了。您最大的罪魁祸首将是值类型,因为值为 1 的盒装 int 不会与另一个具有相同值的盒装 int 具有引用相等性。

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

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

    您也可以使用反射来动态调用由提供的Type 变量在运行时确定的泛型类型上的Default 属性,但如果我不这样做,我不想这样做必须出于性能和编译时安全(或缺乏)的原因。

    【讨论】:

    • 感谢亚当!是的,被比较的类型总是完全相同的。 IComparable 可能是要走的路,尽管我不知道。您能否提供一个简短的代码 sn-p 让我开始。另外,这种方法会考虑 Nullable 类型吗?
    • 嘿亚当...现在我想起来了,我不确定如何实现 IComparable。我正在使用 LINQ2SQL 实体,它们没有实现该接口。我传入的值能否实现,如何实现?
    • 是的,您会在单个值上使用 IComparable,而不是实体本身。
    【解决方案3】:

    你需要测试的类型列表是预先确定的列表吗?如果是这样,您可以使用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);
    }
    

    accept方法的基本实现是:

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

    因为实现 Accept() 的类型知道它是什么类型,所以使用了正确的重载(或泛型类型)。您可以使用反射和查找表(或选择语句)来实现相同的目的,但这更简洁。此外,您不必在不同实现之间重复查找 - 各种类可以实现 IVisitor 以创建特定于类型的功能。

    访客模式是执行“双重调度”的一种方式。 this question 的答案是另一种方式,您也许可以将其转变为适合您特定情况的内容。

    基本上,对您的问题的冗长未回答,抱歉。 :) 这个问题让我很感兴趣——比如你怎么知道你应该测试实体上的什么属性?

    【讨论】:

    • 哈哈,Talljoe 没问题,感谢您的提醒。我正在尝试反思,但如果你稍后能提供一个例子那就太棒了,因为我不完全确定如何做你所描述的。另外,我猜类型列表是每个实体预先确定的,尽管列表不一定包含所有类型,并且每个实体当然会有所不同
    • 酷,感谢 talljoe 的解释,非常感谢。至于您的问题,您会知道要检查哪些文件,因为实际上,我实际上会发送字典,其中键是实体属性名,值是要检查的值。
    • 访客是绝对不是这里应该使用的东西。实际上,您在这里使用它是在重新实现内置的虚拟方法分派。
    • 虚拟方法调度只在被调用者改变行为时起作用。当 调用者 想要根据被调用者的类型改变行为时,您使用访问者模式。
    猜你喜欢
    • 2011-08-26
    • 1970-01-01
    • 2010-10-15
    • 1970-01-01
    • 2018-01-02
    • 2011-05-20
    • 2016-03-10
    • 1970-01-01
    • 2019-11-10
    相关资源
    最近更新 更多