【问题标题】:How to deal with private member accessor and collections?如何处理私有成员访问器和集合?
【发布时间】:2010-02-01 17:35:25
【问题描述】:

我有这样的类层次结构

public class A
{
    protected class B 
    {
        String Name { get; set; }
    }

    protected class C : KeyedCollection<String, B> 
    {
        // ...
    }

    protected C Collection { get; }

    // ...

    public A Copy () 
    {
        // Creates a deep copy of this instance.
    }
}

现在我想编写一个单元测试来比较 A 的两个实例是否在属性 KeyedCollection 中具有相同的项目 B。但是,我无法在 A 实例中执行 foreach 循环。我尝试过的,

[TestClass]
public class TestClass
{
    public void ATest()
    {
        A original = new A();
        A copy = A.Copy();

        // ...

        A_Accessor originalAccessor = A_Accessor.AttachShadow(original);
        A_Accessor copyAccessor = A_Accessor.AttachShadow(copy);

        foreach(var originalItem in originalAccessor.Collection)
        {
            var copyItem = copyAccessor[originalItem.Name];
            Assert.AreEqual(originalItem, copyItem);
        }
    }
}

此代码甚至无法编译,因为 C 类访问器没有实现 IEnumerable 接口(它没有实现 KeyedCollection 类的任何接口)。有谁知道我该如何克服这个问题?

我收到的错误消息是

foreach 语句无法对“C”类型的变量进行操作,因为“A_Accessor.C”不包含“GetEnumerator”的公共定义

【问题讨论】:

    标签: c# visual-studio-2008 unit-testing private-members generic-interface


    【解决方案1】:

    我只是试图编译你的例子:正如预期的那样,我得到了一个错误

    Inconsistent accessibility: field type 'A.C' is less accessible than field 'A.Collection'

    基本上,这意味着您不能使用私有类型声明受保护的属性。所以这不是你的测试代码的问题,而是要测试的代码......

    编辑

    您可以使用originalAccessor.Collection.Target 并将其转换为ICollection。当然,在这种情况下您只能枚举objects,因此您必须再次投射每个项目:

    foreach (var item in (originalAccessor.Collection.Target as ICollection)) {
       A_Accessor.B casted = A_Accessor.B.AttachShadow(item);
       var copyItem = copyAccessor[casted.Name];
       Assert.AreEqual(casted, copyItem);
    }
    

    【讨论】:

    • 原代码不正确。我已经更新了。 B 和 C 两个类都受保护而不是私有。
    • 更多信息,我得到的编译错误在 foreach 循环行。
    • 我找到了一个与您的非常相似的解决方案。语句 var copyItem = copyAccessor[casted.Name] 将不起作用,因为 C_Accessor 没有实现 String 类型的索引器。我通过使用带有谓词的 First 扩展方法来克服它,以返回正确的项目。感谢您的帮助!
    【解决方案2】:

    尚不清楚您是如何设法通过受保护的属性公开私有类类型的,但作为 C 派生自 KeyedCollection 它应该已经继承了 IEnumerable&lt;B&gt; 的实现.

    目前还不清楚您要做什么,但您仍然应该能够迭代集合......如果您甚至可以看到该属性。我怀疑您的代码由于其他原因无法编译 - 因为 C 是根据私有成员类型声明的,尽管受到保护,并且因为您首先尝试从不同的类访问 C (尽管它受到保护)。

    【讨论】:

    • 通过受保护的成员公开私有类是正确的。我直接在这里写代码,而不是复制粘贴,因为我需要从原始代码中删除许多不必要的东西。但是,这些类都受到保护。我正在更新帖子。
    • 我要做的是测试复制方法是否创建了 A 实例的深层副本。但是我在比较单元测试中的 A 类的状态成员时遇到了一些困难,因为 Visual Studio 生成的访问器类的行为不符合预期。例如,C_Accessor 类没有字符串类型的索引器。
    【解决方案3】:

    其实我找到的解决方案和马丁的建议很相似:

    var originalItems = 
        from item in (originalAccessor.Collection.Target as IEnumerable).Cast<Object>()
        select A_Accessor.B.AttachShadow(item);
    
    var copyItems = 
        from item in (copyAccessor.Collection.Target as IEnumerable).Cast<Object>()
        select A_Accessor.B.AttachShadow(item); 
    
    foreach(var original in originalItems) 
    { 
        String originalName = original.Name;
        A_Accessor.B copy = copyItems.First(b => b.Name == originalName);
    
        // ...
    }
    

    感谢您的协助! 卡洛斯。

    【讨论】:

      【解决方案4】:

      在我看来,您正在测试一个实现细节,而不是为您的库用户测试的预期 API 级别。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多