Как создать экземпляр PrivateType внутреннего частного класса

Я пытался настроить модульный тест для частного внутреннего класса, но не имел большого успеха:

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. Как видите, я пытаюсь вписать дату + тип в одно целое.


person Steffen    schedule 08.07.2010    source источник
comment
В C# новый SomeType(args) не использует отражение. Вместо этого это всегда вызов конструктора и вызывает один из доступных конструкторов. В вашем примере кода нет конструктора, поэтому единственным доступным конструктором является конструктор по умолчанию (пустой конструктор - конструктор без аргументов). Вы также не можете вызвать это извне, потому что класс является закрытым. Так что вы правы, что вам нужно отражение.   -  person jyoungdev    schedule 08.07.2010
comment
Связанный вопрос: stackoverflow .com/questions/3198912/   -  person jyoungdev    schedule 08.07.2010
comment
Я не совсем понимаю, почему вы упоминаете часть о конструкторах? Я имею в виду, что PrivateType использует Reflection, так это то, что он использует его для доступа к частному классу/методам. (Это довольно очевидно на странице MSDN: msdn.microsoft.com/en-us/library/, поскольку для этого требуется ReflectionPermission. Что касается соответствующего вопроса, да, я знаю, что не следует тестировать частные классы, но поскольку он используется только внутренне, и очень важно, чтобы GetHashCode всегда возвращал что-то уникальное, я решил, что мне лучше провести тест.   -  person Steffen    schedule 08.07.2010
comment
О! Ты прав. Я неправильно прочитал код. Я думал, вы по какой-то причине добавили новый DailyStatKey(...).   -  person jyoungdev    schedule 09.07.2010
comment
Справедливо, тогда я понял :-)   -  person Steffen    schedule 10.07.2010


Ответы (4)


Сам нашел решение:

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);
person Steffen    schedule 08.07.2010

Вы также можете напрямую использовать PrivateType:

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

Вложенные классы имеют строковый формат, который отличается от их пространства имен (которым является Stats.Model.DailyStat.DailyStatKey), поэтому использование не очевидно.

person Jake    schedule 27.03.2014

Поскольку он частный, единственный класс, который может создать экземпляр, — это сам DailyStat. Если вы не сделаете это не приватным отражением (активатор), это будет вашим единственным выбором, если вы хотите создать класс, хотя это не будет хорошей идеей, поскольку вы не сможете использовать его напрямую, если вы не сможете сделать его достаточно общедоступным тип или интерфейс

РЕДАКТИРОВАТЬ:

Поскольку вы пытаетесь сделать это для модульного тестирования, вам не следует тестировать этот класс, поскольку он является закрытым. Вы сможете протестировать его только через любой общедоступный интерфейс DailyStat.

person aqwert    schedule 08.07.2010
comment
Хм, но PrivateType действительно использует Reflection AFAIK, поэтому я до сих пор не понимаю, почему это проблема. Кроме того, для чего был сделан PrivateType, если не для этого? Вы не можете иметь частный класс непосредственно в пространстве имен. - person Steffen; 08.07.2010
comment
У вас могут быть только частные внутренние классы, которые полезны, если вы хотите инкапсулировать функциональность, которую будет использовать только внешний класс. Вы можете использовать new PrivateType() только внутри класса, в котором он закодирован. Рефлексия не задействована. Я упомянул об отражении, так как его можно использовать для инстатирования частных классов и вызова частных методов вне класса. - person aqwert; 08.07.2010
comment
Именно для инкапсуляции функциональности, торжественно используемой внутри внешнего класса. Так что с этой частью все в порядке. Я проверю, работает ли PrivateType внутри внешнего класса. - person Steffen; 08.07.2010
comment
Теперь понятно, как получить тип: var parentType = typeof(DailyStat); var keyType = parentType.NestedType(DailyKeyStat, BindingFlags.NonPublic); Работает как шарм :-) - person Steffen; 08.07.2010
comment
@ apollodude217: Не думал об этом, он опубликован сейчас, и я приму его, когда 2-дневный лимит истечет. - person Steffen; 08.07.2010

Вы можете закодировать общедоступный метод 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();
person x77    schedule 08.07.2010
comment
Это невозможно, потому что тип, который вы возвращаете, невидим для вызывающей стороны. Также вы можете просто сделать DailyStatKey общедоступным, если хотите. - person Dykam; 08.07.2010
comment
Как говорит Дайкам, это не сработает, к тому же я могу просто пойти с общедоступным внутренним классом, чего я действительно не хочу. - person Steffen; 08.07.2010
comment
Вы можете поместить интерфейс во внутренний класс, который является общедоступным, чтобы вы могли контролировать то, что видно миру. - person aqwert; 08.07.2010
comment
@aqwert, какая от этого польза? Вы также можете ограничить экспозицию в самом классе. - person Dykam; 14.07.2010