【问题标题】:How to instantiate PrivateType of inner private class如何实例化内部私有类的 PrivateType
【发布时间】:2010-07-08 05:30:56
【问题描述】:

我试图为私有内部类设置单元测试,但收效甚微:

namespace Stats.Model
{
  public class DailyStat
  {
    private class DailyStatKey // The one to test
    {
      private DateTime date;
      public DateTime Date 
      { 
        get { return date; }
        set { date = value.Date; }
      }

      public StatType Type { get; set; }

      public override int GetHashCode()
      {
        return Date.Year * 1000000 +
               Date.Month * 10000 +
               Date.Day * 100 +
               (int)Type;
      }

      public override bool Equals(object obj)
      {
        DailyStatKey otherKey = obj as DailyStatKey;
        if (otherKey == null)
          return false;
        return (this.Date == otherKey.Date && this.StatType == otherKey.StatType);
      }
    }
  }
}

我试过这段代码:

PrivateType statKeyType = new PrivateType("Stats.Model", "Stats.Model.DailyStat.DailyStatKey");

还有

PrivateType statKeyType = new PrivateType("Stats.Model", "DailyStat.DailyStatKey");

无济于事。

程序集的名称是“Stats.Model”,对我来说类型名称看起来也正确,但我只是得到一个异常:“System.TypeLoadException:无法加载类型”

那我做错了什么?

据我所知,PrivateType 是基于反射的,我猜它几乎适用于这种情况,因为您不能在命名空间下直接拥有私有类。

编辑:

添加了 DailyStatKey 的完整实现。我要测试的是我的 GetHashCode 方法的唯一性。如您所见,我尝试将日期 + 类型放入单个 int 中。

【问题讨论】:

  • 在 C# 中,“new SomeType(args)”不使用反射。相反,它始终是构造函数调用,并调用可用的构造函数之一。在您的示例代码中,没有构造函数,因此唯一可用的构造函数是默认构造函数(空构造函数——没有参数的构造函数)。您也不能从外部调用它,因为该类是私有的。所以你是对的,你需要反思。
  • 我不太清楚你为什么提到关于构造函数的部分?我对使用反射的 PrivateType 的意思是,它使用它来访问私有类/方法。 (这在 MSDN 页面上非常明显:msdn.microsoft.com/en-us/library/… 因为它需要 ReflectionPermission。至于相关问题,是的,我知道不应该测试私有类,但因为它只在内部使用,并且 GetHashCode 始终至关重要返回一些独特的东西,我想我最好做一个测试。
  • 天啊!你是对的。我误读了代码。我以为你出于某种原因输入了“new DailyStatKey(...)”。
  • 很公平,我明白了:-)

标签: c# unit-testing private-members


【解决方案1】:

自己找到了解决方案:

var parentType = typeof(DailyStat);
var keyType = parentType.GetNestedType("DailyKeyStat", BindingFlags.NonPublic); 
//edited to use GetNestedType instead of just NestedType

var privateKeyInstance = new PrivateObject(Activator.CreateInstance(keyType, true));

privateKeyInstance.SetProperty("Date", DateTime.Now);
privateKeyInstance.SetProperty("Type", StatType.Foo);

var hashCode = (int)privateKeyInstance.Invoke("GetHashCode", null);

【讨论】:

    【解决方案2】:

    你也可以直接使用 PrivateType:

    PrivateType statKeyType = new PrivateType("Stats.Model", "Stats.Model.DailyStat+DailyStatKey");
    

    嵌套类的字符串格式与其命名空间(即 Stats.Model.DailyStat.DailyStatKey)不同,因此用法不明显。

    【讨论】:

      【解决方案3】:

      因为它是私有的,所以唯一可以创建实例的类是DailyStat 本身。除非您将其设为非私有反射(激活器),否则如果您想创建该类,那将是您的唯一选择,尽管这不是一个好主意,因为除非您能够将其转换为足够公开,否则您将无法直接使用它类型或接口

      编辑:

      由于您尝试为单元测试执行此操作,因此您不应该有效地测试此类,因为它是私有的。您只能通过DailyStat 的任何公共接口对其进行测试。

      【讨论】:

      • 嗯,但 PrivateType 确实使用反射 AFAIK,所以我仍然不明白为什么会出现问题。除了 PrivateType 是做什么用的,如果不是这个?您不能直接在命名空间中拥有私有类。
      • 你只能有私有内部类,如果你想封装只有外部类会使用的功能,这很有用。您只能在编码它的类中使用 new PrivateType() 。不涉及反射。我提到了反射,因为它可以用来建立私有类并在类外调用私有方法
      • 正是为了封装在外部类内部郑重使用的功能。所以那部分没问题。我会检查 PrivateType 是否在外部类中工作。
      • 现在知道如何获取类型了: var parentType = typeof(DailyStat); var keyType = parentType.NestedType("DailyKeyStat", BindingFlags.NonPublic);像魅力一样工作:-)
      • @apollodude217: 没想到,现在就发了,等2天的限制过去我就接受了。
      【解决方案4】:

      您可以在父类上编写公共“GetDailyStatKey”方法。

      public class DailyStat
      {
          private class DailyStatKey // The one to test 
          {
          }
          public DailyStatKey GetDailyStatKey()
          {
              return new  DailyStatKey();
          }
      }
      

      现在你可以写了:

      DailyStat v = new DailyStat();
      var x =  v.GetDailyStatKey();
      

      【讨论】:

      • 这是不可能的,因为您返回的类型对调用方是不可见的。如果需要,您也可以将 DailyStatKey 设置为公开。
      • 就像 Dykam 说的那样行不通,而且我可以使用公共内部类——我真的不想要。
      • 您可以在内部类上放置一个公开的接口,这样您就可以控制对世界可见的内容
      • @aqwert,这样做有什么好处?您还可以限制课堂本身的曝光。
      猜你喜欢
      • 2012-12-16
      • 1970-01-01
      • 2023-03-07
      • 2014-06-18
      • 2011-05-03
      • 1970-01-01
      • 2013-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多