MSDN

C# 4.0: Named And Optional Arguments

As part of the co-evolution effort of C# and Visual Basic, C# 4.0 introduces Named and Optional Arguments.

First of all, let’s clarify what are arguments and parameters:

  • Method definition parameters are the input variables of the method.
  • Method call arguments are the values provided to the method parameters.

In fact, the C# Language Specification states the following on §7.5:

The argument list (§7.5.1) of a function member invocation provides actual values or variable references for the parameters of the function member.

Given the above definitions, we can state that:

  • Parameters have always been named and still are.
  • Parameters have never been optional and still aren’t.

Named Arguments

Until now, the way the C# compiler matched method call definition arguments with method parameters was by position. The first argument provides the value for the first parameter, the second argument provides the value for the second parameter, and so on and so on, regardless of the name of the parameters. If a parameter was missing a corresponding argument to provide its value, the compiler would emit a compilation error.

For this call:

Greeting("Mr.", "Morgado", 42);

this method:

public void Greeting(string title, string name, int age)

will receive as parameters:

  • title: “Mr.”
  • name: “Morgado”
  • age: 42

What this new feature allows is to use the names of the parameters to identify the corresponding arguments in the form: name:value

Not all arguments in the argument list must be named. However, all named arguments must be at the end of the argument list. The matching between arguments (and the evaluation of its value) and parameters will be done first by name for the named arguments and than by position for the unnamed arguments.

This means that, for this method definition:

public void Method(int first, int second, int third)

this call declaration:

int i = 0;
Method(i, third: i++, second: ++i);

will have this code generated by the compiler:

int i = 0;
int CS$0$0000 = i++;
int CS$0$0001 = ++i;
Method(i, CS$0$0001, CS$0$0000);

which will give the method the following parameter values:

  • first: 2
  • second: 2
  • third: 0

Notice the variable names. Although invalid being invalid C# identifiers, they are valid .NET identifiers and thus avoiding collision between user written and compiler generated code.

Besides allowing to re-order of the argument list, this feature is very useful for auto-documenting the code, for example, when the argument list is very long or not clear, from the call site, what the arguments are.

Optional Arguments

Parameters can now have default values:

public void Method(int first, int second = 2, int third = 3)

Parameters with default values must be the last in the parameter list and its value is used as the value of the parameter if the corresponding argument is missing from the method call declaration.

For this call declaration:

int i = 0;
Method(i, third: ++i);

will have this code generated by the compiler:

int i = 0;
int CS$0$0000 = ++i;
Method(i, 2, CS$0$0000);

which will give the method the following parameter values:

  • first: 1
  • second: 2
  • third: 1

Because, when method parameters have default values, arguments can be omitted from the call declaration, this might seem like method overloading or a good replacement for it, but it isn’t.

Although methods like this:

public StreamReader OpenTextFile(
    string path,
    Encoding encoding = null,
    bool detectEncoding = true,
    int bufferSize = 1024)

allow to have its calls written like this:

OpenTextFile("foo.txt", Encoding.UTF8);
OpenTextFile("foo.txt", Encoding.UTF8, bufferSize: 4096);
OpenTextFile(
    bufferSize: 4096,
    path: "foo.txt",
    detectEncoding: false);

The complier handles default values like constant fields taking the value and useing it instead of a reference to the value. So, like with constant fields, methods with parameters with default values are exposed publicly (and remember that internal members might be publicly accessible – InternalsVisibleToAttribute). If such methods are publicly accessible and used by another assembly, those values will be hard coded in the calling code and, if the called assembly has its default values changed, they won’t be assumed by already compiled code.

At the first glance, I though that using optional arguments for “bad” written code was great, but the ability to write code like that was just pure evil. But than I realized that, since I use private constant fields, it’s OK to use default parameter values on privately accessed methods.

C# 4.0: Covariance And Contravariance In Generics Made Easy

In my last post, I went through what is variance in .NET 4.0 and C# 4.0 in a rather theoretical way.

Now, I’m going to try to make it a bit more down to earth.

Given:

class Base { }

class Derived : Base { }

Such that:

Trace.Assert(typeof(Base).IsClass && typeof(Derived).IsClass && typeof(Base).IsGreaterOrEqualTo(typeof(Derived)));

  • Covariance

    interface ICovariantIn<out T> { }

    Trace.Assert(typeof(ICovariantIn<Base>).IsGreaterOrEqualTo(typeof(ICovariantIn<Derived>)));

  • Contravariance

    interface IContravariantIn<in T> { }

    Trace.Assert(typeof(IContravariantIn<Derived>).IsGreaterOrEqualTo(typeof(IContravariantIn<Base>)));

  • Invariance

    interface IInvariantIn<T> { }

    Trace.Assert(!typeof(IInvariantIn<Base>).IsGreaterOrEqualTo(typeof(IInvariantIn<Derived>))
        && !typeof(IInvariantIn<Derived>).IsGreaterOrEqualTo(typeof(IInvariantIn<Base>)));

Where:

public static class TypeExtensions
{
    public static bool IsGreaterOrEqualTo(this Type self, Type other)
    {
        return self.IsAssignableFrom(other);
    }
}

C# 4.0: Covariance And Contravariance In Generics

C# 4.0 (and .NET 4.0) introduced covariance and contravariance to generic interfaces and delegates. But what is this variance thing?

According to Wikipedia, in multilinear algebra and tensor analysis, covariance and contravariance describe how the quantitative description of certain geometrical or physical entities changes when passing from one coordinate system to another.(*)

But what does this have to do with C# or .NET?

In type theory, a the type T is greater (>) than type S if S is a subtype (derives from) T, which means that there is a quantitative description for types in a type hierarchy.

So, how does covariance and contravariance apply to C# (and .NET) generic types?

In C# (and .NET), variance is a relation between a generic type definition and a particular generic type parameter.

Given two types Base and Derived, such that:

  • There is a reference (or identity) conversion between Base and Derived
  • Base Derived

A generic type definition Generic<T> is:

  • covariant in T if the ordering of the constructed types follows the ordering of the generic type parameters: Generic<Base> ≥ Generic<Derived>.
  • contravariant in T if the ordering of the constructed types is reversed from the ordering of the generic type parameters: Generic<Base> ≤ Generic<Derived>.
  • invariant in T if neither of the above apply.

If this definition is applied to arrays, we can see that arrays have always been covariant in relation to the type of the elements because this is valid code:

object[] objectArray = new string[] { "string 1", "string 2" };
objectArray[0] = "string 3";
objectArray[1] = new object();

However, when we try to run this code, the second assignment will throw an ArrayTypeMismatchException. Although the compiler was fooled into thinking this was valid code because an object is being assigned to an element of an array of object, at run time, there is always a type check to guarantee that the runtime type of the definition of the elements of the array is greater or equal to the instance being assigned to the element. In the above example, because the runtime type of the array is array of string, the first assignment of array elements is valid because string ≥ string and the second is invalid because string ≤ object.

This leads to the conclusion that, although arrays have always been covariant in relation to the type of the elements, they are not safely covariant – code that compiles is not guaranteed to run without errors.

In C#, variance is enforced in the declaration of the type and not determined by the usage of each the generic type parameter.

Covariance in relation to a particular generic type parameter is enforced, is using the out generic modifier:

public interface IEnumerable<out T>
{
    IEnumerator<T> GetEnumerator();
}

public interface IEnumerator<out T>
{
    T Current { get; }
    bool MoveNext();
}

Notice the convenient use the pre-existing out keyword. Besides the benefit of not having to remember a new hypothetic covariant keyword, out is easier to remember because it defines that the generic type parameter can only appear in output positions — read-only properties and method return values.

In a similar way, the way contravariance is enforced in relation a particular generic type parameter, is using the in generic modifier:

public interface IComparer<in T>
{
    int Compare(T x, T y);
}

Once again, the use of the pre-existing in keyword makes it easier to remember that the generic type parameter can only be used in input positions — write-only properties and method non ref and non out parameters.

A generic type parameter that is not marked covariant (out) or contravariant (in) is invariant.

Because covariance and contravariance applies to the relation between a generic type definition and a particular generic type parameter, a generic type definition can be both covariant, contravariant and invariant depending on the generic type parameter.

public delegate TResult Func<in T, out TResult>(T arg);

In the above delegate definition, Func<T, TResult> is contravariant in T and convariant in TResult.

All the types in the .NET Framework where variance could be applied to its generic type parameters have been modified to take advantage of this new feature.

In summary, the rules for variance in C# (and .NET) are:

  • Variance in relation to generic type parameters is restricted to generic interface and generic delegate type definitions.
  • A generic interface or generic delegate type definition can be covariant, contravariant or invariant in relation to different generic type parameters.
  • Variance applies only to reference types: a IEnumerable<int> is not an IEnumerable<object>.
  • Variance does not apply to delegate combination. That is, given two delegates of types Action<Derived> and Action<Base>, you cannot combine the second delegate with the first although the result would be type safe. Variance allows the second delegate to be assigned to a variable of type Action<Derived>, but delegates can combine only if their types match exactly.

If you want to learn more about variance in C# (and .NET), you can always read:

Note: Because variance is a feature of .NET 4.0 and not only of C# 4.0, all this also applies to Visual Basic 10.

The Evolution Of C#

The Evolution Of C#The first release of C# (C# 1.0) was all about building a new language for managed code that appealed, mostly, to C++ and Java programmers.

The second release (C# 2.0) was mostly about adding what wasn’t time to built into the 1.0 release. The main feature for this release was Generics.

The third release (C# 3.0) was all about reducing the impedance mismatch between general purpose programming languages and databases. To achieve this goal, several functional programming features were added to the language and LINQ was born.

Going forward, new trends are showing up in the industry and modern programming languages need to be more:

  • Declarative

    With imperative languages, although having the eye on the what, programs need to focus on the how. This leads to over specification of the solution to the problem in hand, making next to impossible to the execution engine to be smart about the execution of the program and optimize it to run it more efficiently (given the hardware available, for example).

    Declarative languages, on the other hand, focus only on the what and leave the how to the execution engine. LINQ made C# more declarative by using higher level constructs like orderby and group by that give the execution engine a much better chance of optimizing the execution (by parallelizing it, for example).

  • Concurrent

    Concurrency is hard and needs to be thought about and it’s very hard to shoehorn it into a programming language. Parallel.For (from the parallel extensions) looks like a parallel for because enough expressiveness has been built into C# 3.0 to allow this without having to commit to specific language syntax.

  • Dynamic

    There was been lots of debate on which ones are the better programming languages: static or dynamic. The fact is that both have good qualities and users of both types of languages want to have it all.

All these trends require a paradigm switch. C# is, in many ways, already a multi-paradigm language. It’s still very object oriented (class oriented as some might say) but it can be argued that C# 3.0 has become a functional programming language because it has all the cornerstones of what a functional programming language needs. Moving forward, will have even more.

Besides the influence of these trends, there was a decision of co-evolution of the C# and Visual Basic programming languages. Since its inception, there was been some effort to position C# and Visual Basic against each other and to try to explain what should be done with each language or what kind of programmers use one or the other. Each language should be chosen based on the past experience and familiarity of the developer/team/project/company and not by particular features.

In the past, every time a feature was added to one language, the users of the other wanted that feature too. Going forward, when a feature is added to one language, the other will work hard to add the same feature. This doesn’t mean that XML literals will be added to C# (because almost the same can be achieved with LINQ To XML), but Visual Basic will have auto-implemented properties.

Most of these features require or are built on top of features of the .NET Framework and, the focus for C# 4.0 was on dynamic programming. Not just dynamic types but being able to talk with anything that isn’t a .NET class.

Also introduced in C# 4.0 is co-variance and contra-variance for generic interfaces and delegates.

Stay tuned for more on the new C# 4.0 features.

C# Proposal: Compile Time Static Checking Of Dynamic Objects

C# 4.0 introduces a new type: dynamic. dynamic is a static type that bypasses static type checking.

This new type comes in very handy to work with:

Because static type checking is bypassed, this:

dynamic dynamicValue = GetValue();
dynamicValue.Method();

is equivalent to this:

object objectValue = GetValue();
objectValue
    .GetType()
        .InvokeMember(
            "Method",
            BindingFlags.InvokeMethod,
            null,
            objectValue,
            null);

Apart from caching the call site behind the scenes and some dynamic resolution, dynamic only looks better. Any typing error will only be caught at run time.

In fact, if I’m writing the code, I know the contract of what I’m calling. Wouldn’t it be nice to have the compiler do some static type checking on the interactions with these dynamic objects?

Imagine that the dynamic object that I’m retrieving from the GetValue method, besides the parameterless method Method also has a string read-only Property property. This means that, from the point of view of the code I’m writing, the contract that the dynamic object returned by GetValue implements is:

string Property { get; }
void Method();

Since it’s a well defined contract, I could write an interface to represent it:

interface IValue
{
    string Property { get; }
    void Method();
}

If dynamic allowed to specify the contract in the form of dynamic(contract), I could write this:

 

dynamic(IValue) dynamicValue = GetValue();
dynamicValue.Method();

This doesn’t mean that the value returned by GetValue has to implement the IValue interface. It just enables the compiler to verify that dynamicValue.Method() is a valid use of dynamicValue and dynamicValue.OtherMethod() isn’t.

If the IValue interface already existed for any other reason, this would be fine. But having a type added to an assembly just for compile time usage doesn’t seem right. So, dynamic could be another type construct. Something like this:

 

dynamic DValue
{
    string Property { get; }
    void Method();
}

The code could now be written like this;

 

DValue dynamicValue = GetValue();
dynamicValue.Method();

The compiler would never generate any IL or metadata for this new type construct. It would only be used, at compile time, for static checking of dynamic objects. As a consequence, it makes no sense to have public accessibility, so it would not be allowed.

Once again, if the IValue interface (or any other type definition) already exists, it can be used in the dynamic type definition:

 

dynamic DValue : IValue, IEnumerable, SomeClass
{
    string Property { get; }
    void Method();
}

Another added benefit would be IntelliSense.

I’ve been getting mixed reactions to this proposal. What do you think? Would this be useful?

Web Site Globalization With ASP.NET Routing

For those who don’t know, I have this web site http://PauloMorgado.NET/ that I use both as a web presence besides my blogs and a playfield.

Because I write both in English and Portuguese, I wanted the web site to have both English and Portuguese versions. This is easily accomplished by using ASP.NET Globalization and Localization.

But I wanted to do more than guessing the user’s language form her/his web browser languages. I wanted something like the MSDN and TechNet web sites have where the culture is embedded in the URL which makes it easy for the user to choose in which language she/he wants to see the web site.

With the release of the ASP.NET Routing, this is as easy as writing a custom route handler that sets the culture for the request and returns the requested page handler.

Something like this:

public class GlobalizationRouteHandler : global::System.Web.Routing.IRouteHandler
{
    System.Globalization.CultureInfo culture;
    System.Globalization.CultureInfo uiCulture;

    public GlobalizationRouteHandler(System.Globalization.CultureInfo culture)
        : this(culture, culture)
    {
    }

    public GlobalizationRouteHandler(CultureInfo culture, CultureInfo uiCulture)
    {
        if (culture == null)
        {
            throw new ArgumentNullException("cultureInfo", "cultureInfo is null.");
        }

        if (uiCulture == null)
        {
            throw new ArgumentNullException("uiCulture", "uiCulture is null.");
        }

        this.culture = culture;
        this.uiCulture = uiCulture;
    }

    private GlobalizationRouteHandler()
    {
    }

    #region IRouteHandler Members

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        Thread.CurrentThread.CurrentCulture = this.culture;
        Thread.CurrentThread.CurrentUICulture = this.uiCulture;

        string path = "~/" + (requestContext.RouteData.Values["path"] as string);

        var physicalPath = requestContext.HttpContext.Server.MapPath(path);
        if (System.IO.Directory.Exists(physicalPath))
        {
            path = VirtualPathUtility.Combine(path, "Default.aspx");
        }

        var httpHandler = BuildManager.CreateInstanceFromVirtualPath(path, typeof(IHttpHandler)) as IHttpHandler;

        return httpHandler;
    }

    #endregion
}

And now it’s only a matter of registering the handled cultures:

routes.Add("en", new Route("en/{*path}", new GlobalizationRouteHandler(CultureInfo.GetCultureInfo("en-US"))));
routes.Add("pt", new Route("pt/{*path}", new GlobalizationRouteHandler(CultureInfo.GetCultureInfo("pt-PT"))));

Coupling ASP.NET Session State With Forms Authentication

Today I was talking with João about a way to couple the lifetime of the ASP.NET session state with the lifetime of Forms Authentication ticket.

My idea was to store the session ID in the UserData property of the forms authentication ticket upon logon and retrieve it with a custom session ID manager.

The login code would be something like this:

protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
    bool isPersistent = this.Login1.RememberMeSet;
    string username = this.Login1.UserName;
    var ticket = new FormsAuthenticationTicket(
        0,
        username,
        DateTime.Now,
        DateTime.Now.AddMinutes(2),
        isPersistent,
        Guid.NewGuid().ToString("N"));

    // Encrypt the ticket.
    var encryptedTicket = FormsAuthentication.Encrypt(ticket);

    // Create the cookie.
    this.Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket));

    // Redirect back to original URL.
    this.Response.Redirect(FormsAuthentication.GetRedirectUrl(username, isPersistent));
}

For the purpose of this test I am using a Guid as the session ID.

The session ID manager will return this session ID when queried by the session state HTTP module:

public class SessionIdManager : global::System.Web.SessionState.ISessionIDManager
{
    #region ISessionIDManager Members

    public string CreateSessionID(HttpContext context)
    {
        return GetDummySessionIdOrRedirectToLoginPage(context);
    }

    public string GetSessionID(HttpContext context)
    {
        return GetSessionIdFromFormsIdentity(context);
    }

    public void Initialize()
    {
    }

    public bool InitializeRequest(HttpContext context, bool suppressAutoDetectRedirect, out bool supportSessionIDReissue)
    {
        supportSessionIDReissue = false;
        return GetSessionIdFromFormsIdentity(context) == null;
    }

    public void RemoveSessionID(HttpContext context)
    {
    }

    public void SaveSessionID(HttpContext context, string id, out bool redirected, out bool cookieAdded)
    {
        redirected = false;
        cookieAdded = false;
    }

    public bool Validate(string id)
    {
        return true;
    }

    #endregion

    private static string GetSessionIdFromFormsIdentity(HttpContext context)
    {
        var identity = context.User != null ? context.User.Identity as FormsIdentity : null;

        if ((identity == null) || (identity.Ticket == null) || string.IsNullOrEmpty(identity.Ticket.UserData))
        {
            return GetDummySessionIdOrRedirectToLoginPage(context);
        }
        else
        {
            return identity.Ticket.UserData;
        }
    }

    private static string GetDummySessionIdOrRedirectToLoginPage(HttpContext context)
    {
        if (context.Request.CurrentExecutionFilePath.Equals(FormsAuthentication.DefaultUrl, StringComparison.OrdinalIgnoreCase)
                        || context.Request.CurrentExecutionFilePath.Equals(FormsAuthentication.LoginUrl, StringComparison.OrdinalIgnoreCase))
        {
            return Guid.NewGuid().ToString("N");
        }
        else
        {
            FormsAuthentication.RedirectToLoginPage();
            return null;
        }
    }
}

NOTE: Although this might work, it’s just an intellectual exercise and wasn’t fully tested.

Playing With LINQ: Getting Interface Property Implementations

LINQ With C# (Portuguese)

Today, my friend Nuno was writing some code to get the PropertyInfos of a class implementation of an interface.

Given this interface:

public interface ISomeInterface { int IntProperty { get; set; } string StringProperty { get; } void Method(); }

and this class:

public class SomeClass : ISomeInterface
{
    int ISomeInterface.IntProperty { get; set; }
    public int IntProperty { get; private set; }
    public string StringProperty { get; private set; }
    public void Method() { }
}

Nuno wanted to retrieve:

  • Int32 ISomeInterface.IntProperty
  • System.String StringProperty

The code is fairly simple. First we need to get the interface mappings for the class:

typeof(SomeClass).GetInterfaceMap(typeof(ISomeInterface)).TargetMethods

and get all PropertyInfos for which the MethodInfo in the list is part of the implementation of the property (is either the set method or the get method).

Something like his:

public static bool Implements(this MethodInfo methodInfo, PropertyInfo propertyInfo)
{
    return (propertyInfo.GetGetMethod(true) == methodInfo) || (propertyInfo.GetSetMethod(true) == methodInfo);
}

But what caught my eye was that, with the above extension methods, I can use LINQ to retrieve a list of the desired PropertyInfos.

Something like this:

public static IEnumerable<PropertyInfo> GetInterfacePropertyImplementation(Type implementer, Type implemented)
{
    return (from propertyInfo in implementer.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).AsEnumerable()
            from methodInfo in implementer.GetInterfaceMap(implemented).TargetMethods.AsEnumerable()
            where methodInfo.Implements(propertyInfo)
            select propertyInfo).Distinct();
}

For the sample class and interface, the usage would be something like this:

var q = GetInterfacePropertyImplementation(typeof(SomeClass), typeof(ISomeInterface));

foreach (var p in q)
{
    Console.WriteLine(p);
}

Which would produce the following output:

Int32 ISomeInterface.IntProperty
System.String StringProperty

UPDATED: The previous implementation was overcomplicated and had some string based logic. Kudos to Nuno.

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:

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:

[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:

[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 loose 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:

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:

[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.

ReMIX 09 Is Coming To Lisbon, Portugal

ReMIX 09For the first time, ReMIX is coming to Portugal.

The event will have a keynote and 3 tracks (Web Developer, UX and Architect) with 4 sessions each by the best speakers in each field.

Don’t miss it! Register now!