Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

How To Set Elements Of An Array Of A Private Type Using Visual Studio Shadows

5.00/5 (1 vote)
15 Apr 2010CPOL1 min read 1  
How To Set Elements Of An Array Of A Private Type Using Visual Studio Shadows

Visual Studio uses Publicize to create accessors public for private members and types of a type.

But when you try to set elements of a private array of elements of a private type, things get complicated.

Imagine this hypothetic class to test:

free hit counters
C#
public static class MyClass
{
    private static readonly MyInnerClass[] myArray = new MyInnerClass[10];

    public static bool IsEmpty()
    {
        foreach (var item in myArray)
        {
            if ((item != null) && (!string.IsNullOrEmpty(item.Field)))
            {
                return false;
            }
        }

        return true;
    }

    private class MyInnerClass
    {
        public string Field;
    }
}

If I want to write a test for the case when the array has “non empty” entries, I need to setup the array first.

Using the accessors generated by Visual Studio, I would write something like this:

C#
[TestClass()]
public class MyClassTest
{
    [TestMethod()]
    public void IsEmpty_NotEmpty_ReturnsFalse()
    {
        for (int i = 0; i < 10; i++)
        {
            MyClass_Accessor.myArray[i] = 
		new MyClass_Accessor.MyInnerClass { Field = i.ToString() };
        }

        bool expected = false;
        bool actual;

        actual = MyClass.IsEmpty();

        Assert.AreEqual(expected, actual);
    }
}

But the test will fail because, although the elements of the private array myArray can be read as MyClass_Accessor.MyInnerClass instances, they can't be written as such.

To do so, the test would have to be written like this:

C#
[TestClass()]
public class MyClassTest
{
    [TestMethod()]
    public void IsEmpty_NotEmpty_ReturnsFalse()
    {
        for (int i = 0; i < 10; i++)
        {
            MyClass_Accessor.ShadowedType.SetStaticArrayElement
		("myArray", new MyClass_Accessor.MyInnerClass 
		{ Field = i.ToString() }.Target, i);
        }

        bool expected = false;
        bool actual;

        actual = MyClass.IsEmpty();

        Assert.AreEqual(expected, actual);
    }
}

But, this way, we lose all the strong typing of the accessors because we need to write the name of the array field.

Because the accessor for the field is a property, we could write a set of extension methods that take care of getting the field name for us. Something like this:

C#
public static class PrivateypeExtensions
{
    public static void SetStaticArrayElement<T>
	(this PrivateType self, Expression<Func<T[]>> expression, 
	T value, params int[] indices)
    {
        object elementValue = (value is BaseShadow) ? 
		(value as BaseShadow).Target : value;

        self.SetStaticArrayElement(
            ((PropertyInfo)((MemberExpression)(expression.Body)).Member).Name,
            elementValue,
            indices);
    }

    public static void SetStaticArrayElement<T>
	(this PrivateType self, Expression<Func<T[]>> 
	expression, BindingFlags invokeAttr, T value, params int[] indices)
    {
        object elementValue = (value is BaseShadow) ? 
		(value as BaseShadow).Target : value;

        self.SetStaticArrayElement(
            ((PropertyInfo)((MemberExpression)(expression.Body)).Member).Name,
            invokeAttr,
            elementValue,
            indices);
    }
}

Now, we can write the test like this:

C#
[TestClass()]
public class MyClassTest
{
    [TestMethod()]
    public void IsEmpty_NotEmpty_ReturnsFalse()
    {
        for (int i = 0; i < 10; i++)
        {
            MyClass_Accessor.ShadowedType.SetStaticArrayElement(() => 
		MyClass_Accessor.myArray, new MyClass_Accessor.MyInnerClass 
		{ Field = i.ToString() }, i);
        }

        bool expected = false;
        bool actual;

        actual = MyClass.IsEmpty();

        Assert.AreEqual(expected, actual);
    }
}

It’s not the same as the first form, but it’s strongly typed and we'll get a compiler error instead of a test run error if we change the name of the myArray field.

You can find this and other tools on the PauloMorgado.TestTools on CodePlex.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)