Прежде всего, я хочу отметить, что у меня уже есть работающее решение, но я пытаюсь выяснить, есть ли способ сделать код чище и менее громоздким.
Вот моя ситуация. Я на самом деле упростил ситуацию и создал поддельный пример, чтобы проиллюстрировать иллюстрацию. Я просто собираюсь выложить конкретный пример, показывающий, что я уже сделал, и это работает.
Предположим, у нас есть эти классы:
public abstract class Shape{ //...elided... }
public class Square : Shape { //...elided... }
public class Circle : Shape { //...elided... }
И предположим, что есть какой-то класс, который делает с ними что-то вроде этого:
public class ShapeThingy
{
public static void MakeSquaresDance(List<Squares> squares){ //...elided... }
public static void RollCircles(List<Circles> circles){ //...elided... }
}
Теперь предположим, что я хочу протестировать класс ShapeThingy. Предположим, что для некоторых тестов я хочу заменить MockSquares и MockCircles на списки вместо квадратов и кругов. Кроме того, предположим, что настройка MockCircles и MockSquares очень похожа, так что я хочу иметь один метод для создания списков фиктивных фигур, и я говорю этому методу тип фигуры, который мне нужен. Вот как я это реализовал:
public class Tests
{
[Test]
public void TestDancingSquares()
{
List<Squares> mockSquares = GetMockShapes<Square, MockSquare>();
ShapeThingy.MakeSquaresDance(mockSquares);
Assert.Something();
}
[Test]
public void TestRollingCircles()
{
List<Circles> mockCircles = GetMockShapes<Circle, MockCircle>();
ShapeThingy.RollCircles(mockCircles );
Assert.Something();
}
private List<TBase> GetMockShapes<TBase, TMock>()
where TBase : Shape
where TMock : TBase, new()
{
List<TBase> mockShapes = new List<TBase>();
for (int i = 0; i < 5; i++)
{
mockShapes.Add(MockShapeFactory.CreateMockShape<TMock>());
}
}
}
public class MockSquare : Square { //...elided... }
public class MockCircle : Circle { //...elided... }
public class MockShapeFactory
{
public static T CreateMockShape<T>()
where T : Shape, new()
{
T mockShape = new T();
//do some kind of set up
return mockShape;
}
}
Теперь это работает нормально. Проблема, с которой я столкнулся, заключается в том, что вы должны указать GetMockShapes () как желаемый тип вывода списка, так и тип макета, который вы действительно хотите, чтобы список содержал. Когда на самом деле я уже знаю, что если я запрашиваю GetMockShapes () для List , то он должен быть заполнен MockSquare. Отчасти громоздко приходится указывать обе вещи снова и снова.
Я хочу сделать что-то вроде этого:
private List<TBase> GetMockShapes<TBase>()
where TBase : Shape
{
List<TBase> mockShapes = new List<TBase>();
Type mockType = getAppropriateMockType<TBase>();
for (int i = 0; i < 5; i++)
{
//compiler error: typeof(mockType) doesn't work here
mockShapes.Add(MockShapeFactory.CreateMockShape<typeof(mockType)>());
}
}
private Type getAppropriateMockType<TBase>()
{
if(typeof(TBase).Equals(typeof(Square)))
{
return typeof(MockSquare);
}
if(typeof(TBase).Equals(typeof(Circle)))
{
return typeof(MockCircle);
}
//else
throw new ArgumentException(typeof(TBase).ToString() + " cannot be converted to a mock shape type.");
}
//add then a test would look like this
//(one less word, one less chance to screw up)
[Test]
public void TestDancingSquares()
{
List<Squares> mockSquares = GetMockShapes<Square>();
ShapeThingy.MakeSquaresDance(mockSquares);
Assert.Something();
}
Проблема в том, что версия не будет компилироваться, и я не могу найти способ обойти это. Может быть, то, что я хочу сделать, не возможно.
Теперь в этот момент вы можете подумать: «Если он просто использует IEnumerable вместо List , тогда он может воспользоваться ковариацией в C # 4.0, и ему не нужно будет ничего делать с этим , «это правда, но в нашем реальном коде мы используем не List , а скорее пользовательский конкретный тип, Something (и это не коллекция в стиле IEnumerable), и я не иметь возможность изменить использование Something и ввести ковариантный интерфейс ISomething прямо сейчас.
В любом случае, все, что я пытаюсь сделать, я думаю, это попытаться избавить себя от необходимости набирать одно лишнее слово всякий раз, когда я вызываю GetMockShapes (), так что это не так уж и сложно, и я не знаю, может быть, это Хорошо, что оба типа указаны так, что это видно. Я просто подумал, что было бы здорово, если бы я мог найти какой-то способ сделать это, и я бы также узнал что-то новое. Я в основном хочу знать, можно ли это сделать, чтобы удовлетворить мое любопытство. Я не думаю, что это действительно так важно с точки зрения качества кода.