Невозможно присвоить null анонимному свойству массива типов
У меня есть любой массив объектов (Pilot
) с свойством (Hanger
), который может быть нулевым, который сам имеет свойство (List<Plane>
). В целях тестирования я хочу упростить и "сгладить" это анонимному объекту со свойствами PilotName
(string) и Planes
(array), но не уверен, как обрабатывать свойство null Hanger
или пустой PlanesList
,
(Почему анонимные объекты? Поскольку объекты API, которые я тестирую, доступны только для чтения, и я хочу, чтобы тест был "декларативным": автономным, простым и читаемым... но я открыт для других предложений. Также я пытаюсь узнать больше о LINQ.)
Пример
class Pilot
{
public string Name;
public Hanger Hanger;
}
class Hanger
{
public string Name;
public List<Plane> PlaneList;
}
class Plane
{
public string Name;
}
[TestFixture]
class General
{
[Test]
public void Test()
{
var pilots = new Pilot[]
{
new Pilot() { Name = "Higgins" },
new Pilot()
{
Name = "Jones", Hanger = new Hanger()
{
Name = "Area 51",
PlaneList = new List<Plane>()
{
new Plane { Name = "B-52" },
new Plane { Name = "F-14" }
}
}
}
};
var actual = pilots.Select(p => new
{
PilotName = p.Name,
Planes = (p.Hanger == null || p.Hanger.PlaneList.Count == 0) ? null : p.Hanger.PlaneList.Select(h => ne
{
PlaneName = h.Name
}).ToArray()
}).ToArray();
var expected = new[] {
new { PilotName = "Higgins", Planes = null },
new
{
PilotName = "Jones",
Planes = new[] {
new { PlaneName = "B-52" },
new { PlaneName = "F-14" }
}
}
};
Assert.That(actual, Is.EqualTo(expected));
}
Непосредственная проблема заключается в том, что строка expected... Planes = null
ошибок с,
Невозможно назначить свойство анонимного типа, но признать, что основная проблема может заключаться в том, что использование null
in actual
использует null
не лучший подход в первую очередь.
Любые идеи о том, как назначить нулевой массив в expected
или использовать другой подход, чем null
в actual
?
Ответы
Ответ 1
Происходит две вещи:
Во-первых, когда вы создаете экземпляр анонимного типа с помощью new { Name = Value}
, чтобы построить тип, компилятор должен уметь выработать тип Value
. Просто null
сам по себе не имеет типа, поэтому компилятор не знал бы, какой тип должен предоставить ваш член Planes
.
Теперь, если вы использовали именованный тип для значения, вы могли бы просто сказать (type)null
и сделать это, НО, потому что вам нужен массив другого анонимного типа, нет способа ссылаться на него (это анонимно!),
Итак, как вы получаете null
в виде массива анонимного типа? Ну, спецификация С# гарантирует, что анонимные типы с членами имеют одинаковые имена и типы (в том же порядке!) унифицированы; т.е. если мы скажем
var a = new { Foo = "Bar" };
var b = new { Foo = "Baz" };
то a
и b
имеют тот же тип. Мы можем использовать этот факт, чтобы получить подходящий тип null
таким образом:
var array = (new[] { new { PlaneName = "" } });
array = null;
Это не очень хорошо, но он работает - теперь array
имеет правильный тип, но null
значение. Таким образом, это компилируется:
var array = new[] { new { PlaneName = "" } };
array = null;
var expected = new[]
{
new
{
PilotName = "Higgins",
Planes = array
},
new
{
PilotName = "Higgins",
Planes = new[]
{
new { PlaneName = "B-52" },
new { PlaneName = "F-14" }
}
}
};
Ответ 2
Вы должны использовать типизированный null:
(List<Plane>)null
или
(Plane[])null
В противном случае компилятор не знает, какой тип вы хотите, чтобы член анонимного типа был.
Обновление
Как справедливо указал @AakashM - это решает вашу проблему присвоения null
анонимному члену, но на самом деле не компилируется, и если бы это было сделано, это не позволило бы вам обращаться к этим членам.
Исправление должно было бы сделать это (к сожалению, для массива null
и анонимного Planes
потребуется кастинг:
var expected = new[] {
new {
PilotName = "Higgins",
Planes = (IEnumerable)null
},
new {
PilotName = "Higgins",
Planes = (IEnumerable)new [] {
new { PlaneName = "B-52" },
new { PlaneName = "F-14" }
}
}
};
Поэтому используйте IEnumerable
как тип элемента. Вы также можете использовать IEnumerable<object>
, но эффект будет одинаковым в любом случае.
Или - вы можете использовать IEnumerable<dynamic>
как обычный тип - это позволит вам сделать это:
Assert.AreEqual("B-52", expected[1].Planes.First().PlaneName);
Ответ 3
Просто используйте default(Plane[])
вместо null
.