Showing posts with label generics. Show all posts
Showing posts with label generics. Show all posts

The Dark Side of Covariance

What’s wrong with the following code?

var names = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
...
if (names.Contains(sqlCommand.ExecuteScalar())

This  code is intended to check whether the result of a SQL query is contained in a case-insensitive collection of names.  However, if you run this code, the resulting check will be case-sensitive.  Why?

As you may have guessed from the title, this is caused by covariance.  In fact, this code will not compile at all against .Net 3.5. 

The problem is that ExecuteScalar() returns object, not string.  Therefore, it doesn’t call HashSet<string>.Contains(string), which is what it’s intending to call (and which uses the HashSet’s comparer).  Instead, on .Net 4.0, this calls the  Enumerable.Contains<object>(IEnumerable<object>, string) extension method, using the covariant conversion from IEnumerable<string> to IEnumerable<object>.  Covariance allows us to pass object to the Contains method of any strongly-typed collection (of reference types).

Still, why is it case-sensitive?  As Jon Skeet points out, the LINQ Contains() method is supposed to call any built-in Contains() method from ICollection<T>, so it should still use the HashSet’s case-insensitive Contains().

The reason is that although HashSet<String> implements ICollection<string>, it does not implement ICollection<object>.  Since we’re calling Enumerable.Contains<object>, it checks whether the sequence implements ICollection<object>, which it doesn’t.  (ICollection<T> is not covariant, since it allows write access)

Fortunately, there’s a simple fix: just cast the return value back to string (and add a comment explaining the problem).  This allows the compiler to call HashSet<string>.Contains(string), as was originally intended.

//Call HashSet<string>.Contains(string), not the
//covariant Enumerable.Contains(IEnumerable<object>, object)
//http://blog.slaks.net/2011/12/dark-side-of-covariance.html
if (names.Contains((string)sqlCommand.ExecuteScalar())
(I discovered this issue in my StringListConstraint for ASP.Net MVC)

Generic Base Classes in ASP.Net MVC

Last time, we saw that there are severe limitations in creating ASPX pages which inherit generic base classes.  Many readers were probably wondering how ASP.Net MVC works around this limitation.  In ASP.Net MVC views, people write pages like this all the time:

<%@ Page Language="C#" 
    Inherits="ViewPage<IEnumerable<DataLayer.Product>>" %>

ASP.Net MVC includes its own workaround for these limitations.  The Web.config file in the Views folder of an ASP.Net MVC project registers a PageParserFilter:

<pages
    validateRequest="false"
    pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    ...>
    ...
</pages>

PageParserFilter is one of ASP.Net’s lesser-known extensibility points.  It can intercept different parts in the parsing process for an ASPX page and modify the page.  The MVC framework’s ViewTypeParserFilter will check whether the page’s inherits="" attribute contains ( or a < characters; these characters can only appear in C# or VB.Net generic types, but not in the CLR’s native syntax.

If the inherits="" attribute contains these characters, it will save the attribute’s original value, then replace it with ​ViewPage (Or ViewMasterPage or ViewUserControl, as appropriate).  This way, the rest of the built-in ASP.Net parser will see a normal type name that it knows how to parse. After the page finishes parsing, an internal ControlBuilder registered on MVC’s base types (ViewPage, ViewMasterPage or ViewUserControl) will replace the base type in the generated CodeDOM tree with the original value of the inherits="" attribute.

The one problem with the hack is that it leaves the ASPX parsing engine unaware of the page’s actual base type.  Therefore, if you make a page that inherits a generic base class with additional properties, you won’t be able to set those properties in the <%@ Page declaration (since the ASPX parser won’t know about them).  If you inherit a non-generic type, this mechanism will not kick in and page properties will work fine.

Generic Base Classes in ASP.Net

ASP.Net pages can inherit from custom classes (as long as they inherit System.Web.UI.Page).  This can be useful to add utility functions or shared (code-behind) behaviors to your pages.  (Note that you could also use Extension methods or HTTP modules)

However, if you try to inherit a generic base class, it won’t work:

public class DataPage<T> : Page {
    public T Data { get; set; }
}
<%@ Page Language="C#" Inherits="DataPage<string>" %>

This code results in a yellow screen of death, with the  parser error, Could not load type 'DataPage<string>'.

This happens because the ASP.Net page parser is unaware of C# generics syntax.  The familiar generics syntax (eg, List<int>) is actually a C# innovation and is not used at all in the actual framework.  The “native” generics syntax, which is used by Reflection, is markedly different: List`1[Int32] (namespaces omitted for brevity).  This name is returned by the Type.AssemblyQualifiedName property.

Since ASP.Net uses reflection APIs to load types, we need to specify generic types using CLR syntax (and with full namespaces).   Therefore, the following page will work:

<%@ Page Language="C#" 
    Inherits="TestSite.DataPage`1[[System.String, mscorlib]]" %>

However, it’s not so simple.  ASP.Net does not call Type.GetType to parse these strings; instead, it loops over every referenced assembly and calls Assembly.GetType on each one.  This is why you don’t need to include the assembly name whenever using the Inherits attribute (which would have been necessary for Type.GetType)  Ordinarily, this is very useful, but here, it comes back to bite you. 
It is not possible to parse a type from one assembly with a generic parameter from a different assembly using Assembly.GetType, unless the generic parameter is in mscorlib.

Therefore, for example, it is not possible to create an ASPX page that inherits DataPage<DataLayer.Product> if DataLayer.Product is in a different assembly than DataPage.  As a workaround, one can create a non-generic class which inherits DataPage<DataLayer.Product>, then make the ASPX page inherit this temporary class.

Next time: MVC magic

Partial Type Inference in .Net

When designing fluent APIs, one issue that comes up is partial type inference.  If a method has two type parameters, there is no way to call it and only specify one of the type parameters (and leave the other inferred by the compiler)

For example, suppose we are creating a type-safe wrapper around a parameterized SqlCommand.
Ideally, it would be called like this:

using(DbConnection connection = ...) {
    var result = connection.ExecuteScalar<int>(
        "SELECT COUNT(*) FROM TableName WHERE Modified > someDate",
        new { someDate }
    );
}

Where the generic parameter specifies the return type.

In order to implement this efficiently, one would create methods at runtime which add DbParameters for each property in the anonymous type, and store them in a static generic class.   
It would look something like this:

static class Extensions {
    public static void AddParams<TParam>(this IDbCommand command, 
                                         TParam parameters)
        where TParam : class { 
        if (parameters != null) 
            ParamAdders<TParam>.Adder(command, parameters);
    }
    static class ParamAdders<TParam> where TParam : class {
        public delegate void ParamAdder(IDbCommand command,
                                        TParam parameters);
        public static readonly ParamAdder Adder = CreateParamAdder();

        private static ParamAdder CreateParamAdder() { 
            return ...; 
        }
    }
}

However, that requires that the ExecuteScalar extension method take TParam as a generic parameter.  Since anonymous types can only be passed as generic parameters via type inference, this makes it impossible to pass the return type as a generic parameter.

To fix this issue, we can split the generic parameters across two methods.  We can change the extension method to take a single generic parameter, and return a generic class with a method that takes the other generic parameter.

For example:

static class BetterExtensions {
    public static SqlStatement<T> Sql<T>(this IDbConnection conn,
                                         string sqlText) { 
        return new SqlStatement<T>(conn, sqlText); 
    }
}
class SqlStatement<TReturn> : IHideObjectMembers {
    public SqlStatement(IDbConnection connection, string sql) { 
        Connection = connection; 
        Sql = sql;
    }
    public IDbConnection Connection { get; private set; }
    public string Sql { get; private set; }

    public TReturn Execute() { return Execute<object>(null); }
    public TReturn Execute<TParam>(TParam parameters)
         where TParam : class {
        return ...;
    }
}

I use an IHideObjectMembers interface to hide the methods inherited from Object from IntelliSense.  Note that the interface must be defined in an assembly outside of your solution. (or, to be more precise, that isn’t a Project reference)

This version would be called like this:

using(IDbConnection connection = null) {
    var result = connection.Sql<int>(
        "SELECT COUNT(*) FROM TableName WHERE Modified > someDate"
    ).Execute(new { someDate });
}

The return type is specified explicitly in the call to Sql<T>(), and the parameter type is passed implicitly to Execute<TParam>().

Requiring Inherited Types in Generic Constraints

A generic class can specify that its generic parameter must inherit a type.  However, there is no obvious way in general to prevent clients from passing the base type itself.

For example, take the following set of types:

abstract class Entity  { }

class Person : Entity { }
class Boat : Entity { }
class Car : Entity { }

class Repository<TEntity> where TEntity : Entity { }

This allows the type Repository<Entity>, which doesn’t make logical sense.

In this particular case, we could prevent that by changing the generic constraint to where TEntity : Entity, new().  Since the base Entity class is abstract, that would disallow a Repository<Entity>.  However,if the concrete entities also  don’t have default constructors, this wouldn’t work.  Similarly, had the base type been an interface, we could add a : class constraint.

There is an (somewhat) ugly hack that can be used to prevent parameterizations of the base class in arbitrary cases (as long as you control every type involved):

abstract class Entity  { }
interface IConcreteEntity { }    //Marker interface

class Person : Entity, IConcreteEntity { }
class Boat : Entity, IConcreteEntity { }
class Car : Entity, IConcreteEntity { }

class Repository<TEntity> 
      where TEntity : Entity, IConcreteEntity { }

Specifically, we can add a marker interface which is implemented by all concrete implementations of the base type.  We can then constrain a generic parameter to inherit both the base type and the marker interface.  Since the base class itself does implement the marker interface, it will not be valid as a parameter. 
Note that the marker interface must be implemented (perhaps indirectly) by every single concrete implementation.