35

我有任何Pilot具有 () 属性的 () 对象数组Hanger,它可能为 null,它本身具有 ( List<Plane>) 属性。出于测试目的,我想将其简化并“展平”为具有属性PilotName(string) 和Planes(array) 的匿名对象,但不确定如何处理 nullHanger属性或空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错误,

不能分配给匿名类型属性,但承认潜在的问题可能是 using nullin actualis usingnull并不是最好的方法。

任何想法如何分配空数组或采用与inexpected不同的方法?nullactual

4

3 回答 3

73

您必须使用键入 的null

(List<Plane>)null

或者

(Plane[])null

否则编译器不知道你希望匿名类型的成员是什么类型。

更新 正如@AakashM 正确指出的那样 - 这解决了您将 a 分配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);
于 2013-01-14T10:58:48.993 回答
41

There are two things happening:

Firstly, when you construct an instance of an anonymous type using new { Name = Value}, in order to build the type the compiler needs to be able to work out the type of Value. Just null on its own doesn't have a type, so the compiler wouldn't know what type to give your Planes member.

Now, if you were using a named type for the value, you could just say (type)null and be done, BUT because you want an array of another anonymous type, there's no way to refer to is (it's anonymous!).

So how do you get null typed as array of an anonymous type? Well, the C# spec guarantees that anonymous types with members the same names and types (in the same order!) are unified; that is, if we say

var a = new { Foo = "Bar" };
var b = new { Foo = "Baz" };

then a and b have the same type. We can use this fact to get our suitably-typed null thus:

var array = (new[] { new { PlaneName = "" } });
array = null;

It's not pretty but it works - now array has the right type but a null value. So this compiles:

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"
      }
    }
  }
};
于 2013-01-14T11:13:39.177 回答
13

只需使用default(Plane[])而不是null.

于 2013-01-14T11:01:19.510 回答