J'ai un tableau d'objets (Pilot
) avec une propriété (Hanger
), qui peut être nulle, qui a elle-même une (List<Plane>
) propriété. À des fins de test, je veux simplifier et "aplatir" cela en un objet anonyme avec les propriétés PilotName
(chaîne) et Planes
(tableau) mais je ne sais pas comment gérer un null Hanger
ou un PlanesList
vide.
(Pourquoi des objets anonymes? Parce que les objets de l'API que je teste sont en lecture seule et je veux que le test soit "déclaratif": autonome, simple et lisible ... mais je suis ouvert à d'autres suggestions. Aussi je " j'essaie d'en savoir plus sur LINQ.)
exemple
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));
}
Le problème immédiat est que la ligne expected... Planes = null
erreurs avec,
Impossible d'affecter à une propriété de type anonyme mais admettez que le problème sous-jacent peut être que l'utilisation de
null
dansactual
utilisenull
n'est pas la meilleure approche en premier lieu.
Avez-vous des idées sur la façon d'affecter le tableau nul dans expected
ou d'adopter une approche différente de null
dans actual
?
Il se passe deux choses:
Premièrement, lorsque vous construisez une instance d'un type anonyme à l'aide de new { Name = Value}
, afin de construire le type, le compilateur doit être capable de déterminer type de Value
. Juste null
seul n'a pas de type, donc le compilateur ne saurait pas quel type donner à votre membre Planes
.
Maintenant, si vous utilisez un type nommé pour la valeur, vous pouvez simplement dire (type)null
et c'est fait, MAIS parce que vous voulez un tableau d'un autre type anonyme, il n'y a aucun moyen de se référer à is (c'est anonyme!).
Alors, comment obtenez-vous null
tapé en tant que tableau d'un type anonyme? Eh bien, la spécification C # garantit que les types anonymes avec des membres ayant les mêmes noms et types (dans le même ordre!) Sont nifiés; c'est-à-dire, si nous disons
var a = new { Foo = "Bar" };
var b = new { Foo = "Baz" };
alors a
et b
ont le même type. Nous pouvons utiliser ce fait pour obtenir notre null
correctement typé:
var array = (new[] { new { PlaneName = "" } });
array = null;
Ce n'est pas joli mais cela fonctionne - maintenant array
a le droit type mais un null
valeur. Donc, cela compile:
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" }
}
}
};
Vous devez utiliser un tapé null :
(List<Plane>)null
Ou
(Plane[])null
Sinon, le compilateur n'a aucune idée de quel type vous voulez que le membre du type anonyme soit.
pdate Comme @AakashM l'a souligné à juste titre - cela résout votre problème d'attribution d'un null
à un membre anonyme - mais ne compile pas réellement - et s'il le faisait, il ne permettrait pas vous référer à ces membres.
Un correctif serait de faire cela (malheureusement, le tableau null
et le tableau Planes
anonyme devront être castés:
var expected = new[] {
new {
PilotName = "Higgins",
Planes = (IEnumerable)null
},
new {
PilotName = "Higgins",
Planes = (IEnumerable)new [] {
new { PlaneName = "B-52" },
new { PlaneName = "F-14" }
}
}
};
Utilisez donc IEnumerable
comme type de membre. Vous pouvez également utiliser IEnumerable<object>
mais l'effet sera le même dans les deux cas.
Ou - vous pouvez utiliser IEnumerable<dynamic>
comme type commun - cela vous permettrait de faire ceci:
Assert.AreEqual("B-52", expected[1].Planes.First().PlaneName);
Utilisez simplement default(Plane[])
au lieu de null
.