Covariance and Contravariance in Generics

source: Covariance and Contravariance in Generics

Covariant type parameters

The following example illustrates covariant type parameters. The example defines two types: Base has a static method named PrintBases that takes anIEnumerable<Base> (IEnumerable(Of Base) in Visual Basic) and prints the elements. Derived inherits from Base. The example creates an emptyList<Derived> (List(Of Derived) in Visual Basic) and demonstrates that this type can be passed to PrintBases and assigned to a variable of typeIEnumerable<Base> without casting. List implements IEnumerable, which has a single covariant type parameter. The covariant type parameter is the reason why an instance of IEnumerable<Derived> can be used instead of IEnumerable<Base>.

using System;
using System.Collections.Generic;

class Base
{
    public static void PrintBases(IEnumerable<Base> bases)
    {
        foreach(Base b in bases)
        {
            Console.WriteLine(b);
        }
    }
}

class Derived : Base
{
    public static void Main()
    {
        List<Derived> dlist = new List<Derived>();

        Derived.PrintBases(dlist);
        IEnumerable<Base> bIEnum = dlist;
    }
}

Contravariant type parameters

The following example illustrates contravariant type parameters. The example defines an abstract (MustInherit in Visual Basic) Shape class with an Areaproperty. The example also defines a ShapeAreaComparer class that implements IComparer<Shape> (IComparer(Of Shape) in Visual Basic). The implementation of the IComparerCompare method is based on the value of the Area property, so ShapeAreaComparer can be used to sort Shapeobjects by area.

The Circle class inherits Shape and overrides Area. The example creates a SortedSet of Circle objects, using a constructor that takes anIComparer<Circle> (IComparer(Of Circle) in Visual Basic). However, instead of passing an IComparer<Circle>, the example passes aShapeAreaComparer object, which implements IComparer<Shape>. The example can pass a comparer of a less derived type (Shape) when the code calls for a comparer of a more derived type (Circle), because the type parameter of the IComparer generic interface is contravariant.

When a new Circle object is added to the SortedSet<Circle>, the IComparer<Shape>.Compare method (IComparer(Of Shape).Comparemethod in Visual Basic) of the ShapeAreaComparer object is called each time the new element is compared to an existing element. The parameter type of the method (Shape) is less derived than the type that is being passed (Circle), so the call is type safe. Contravariance enables ShapeAreaComparerto sort a collection of any single type, as well as a mixed collection of types, that derive from Shape.

using System;
using System.Collections.Generic;

abstract class Shape
{
    public virtual double Area { get { return 0; }}
}

class Circle : Shape
{
    private double r;
    public Circle(double radius) { r = radius; }
    public double Radius { get { return r; }}
    public override double Area { get { return Math.PI * r * r; }}
}

class ShapeAreaComparer : System.Collections.Generic.IComparer<Shape>
{
    int IComparer<Shape>.Compare(Shape a, Shape b) 
    { 
        if (a == null) return b == null ? 0 : -1;
        return b == null ? 1 : a.Area.CompareTo(b.Area);
    }
}

class Program
{
    static void Main()
    {
        // You can pass ShapeAreaComparer, which implements IComparer<Shape>, 
        // even though the constructor for SortedSet<Circle> expects  
        // IComparer<Circle>, because type parameter T of IComparer<T> is 
        // contravariant.
        SortedSet<Circle> circlesByArea = 
            new SortedSet<Circle>(new ShapeAreaComparer()) 
                { new Circle(7.2), new Circle(100), null, new Circle(.01) };

        foreach (Circle c in circlesByArea)
        {
            Console.WriteLine(c == null ? "null" : "Circle with area " + c.Area);
        }
    }
}

/* This code example produces the following output:

null
Circle with area 0.000314159265358979
Circle with area 162.860163162095
Circle with area 31415.9265358979
 */
Advertisements

Query Syntax, Method Syntax, Standard Query Operators, and Extension Methods in LINQ

Query Syntax and Method Syntax in LINQ (C#)

Standard Query Operators Overview

Extension Methods (C# Programming Guide)

* Pop Quiz for developers: what’s an alternative method syntax (using Lambda expression) of this query syntax? the answer is in one of the linked pages above. =).

var query = from word in words
            group word.ToUpper() by word.Length into gr
            orderby gr.Key
            select new { Length = gr.Key, Words = gr };

Type Relationships in LINQ Query Operations (C#)

source: Type Relationships in LINQ Query Operations (C#) from MSDN LINQ (C#) pages

As a developer, you will need to understand what occurs behind the scenes when variables are implicitly typed by using var. LINQ query operations are strongly typed in the data source, in the query itself, and in the query execution. The type of the variables in the query must be compatible with the type of the elements in the data source and with the type of the iteration variable in the foreach statement. This strong typing guarantees that type errors are caught at compile time when they can be corrected before users encounter them.

Queries that do not Transform the source data: The following illustration shows a LINQ to Objects query operation that performs no transformations on the data. The source contains a sequence of strings and the query output is also a sequence of strings.Relation of data types in a LINQ query

1. The type argument of the data source determines the type of the range variable.
2. The type of the object that is selected determines the type of the query variable. Here name is a string. Therefore, the query variable is an IEnumerable<string>.
3. The query variable is iterated over in the foreach statement. Because the query variable is a sequence of strings, the iteration variable is also a string.

Queries that Transform the source data: The following illustration shows a LINQ to SQL query operation that performs a simple transformation on the data. The query takes a sequence of Customer objects as input, and selects only the Name property in the result. Because Name is a string, the query produces a sequence of strings as output.

A query that transforms the data type

1. The type argument of the data source determines the type of the range variable.
2. The select statement returns the Name property instead of the complete Customer object. Because Name is a string, the type argument of custNameQuery is string, not Customer.
3. Because custNameQuery is a sequence of strings, the foreach loop’s iteration variable must also be a string.

The following illustration shows a slightly more complex transformation. The select statement returns an anonymous type that captures just two members of the original Customer object.

A query that transforms the data type

1. The type argument of the data source is always the type of the range variable in the query.
2. Because the select statement produces an anonymous type, the query variable must be implicitly typed by using var.
3. Because the type of the query variable is implicit, the iteration variable in the foreach loop must also be implicit.

Let the compiler infer the type! : Although you should understand the type relationships in a query operation, you do have the option to let the compiler do all the work for you. The keyword var can be used for any local variable in a query operation. The following illustration is exactly equivalent to example number 2 that was discussed earlier. The only difference is that the compiler will supply the strong type for each variable in the query operation:

Type flow with implicit typing

JSON, JSON.NET Links

JSON : JSON (JavaScript Object Notation) is a lightweight data-interchange text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.

JSON.NET : This is a popular high-performance JSON framework for .NET

JSON.NET Features:
Flexible JSON serializer for converting between .NET objects and JSON
LINQ to JSON for manually reading and writing JSON
High performance, faster than .NET’s built-in JSON serializers
Write indented, easy to read JSON
Convert JSON to and from XML
Supports .NET 2, .NET 3.5, .NET 4, Silverlight, Windows Phone and Windows 8.