During this lesson we’ll talk mostly about generic types and reflection. Also I’ll show a LINQ magic and generic types. This lesson shows only the top of the iceberg, More LINQ features and reflection will be shown in further lessons.

Agenda

LINQ (Language-Integrated Query)

LINQ introduces standard, easily-learned patterns for querying and updating data, and the technology can be extended to support potentially any kind of data store.

All LINQ query operations consist of three distinct actions: - Obtain the data source - Create the query - Execute the query

Examples:

var numbers = new[] {12, 3, 7, 4, 3, 1, 8, 22, 65, 33, 7, 9, 3, 3};

var evenNumbers = numbers.Where(x => x%2 == 0);
foreach (var evenNumber in evenNumbers)
{
    Console.WriteLine(evenNumber);
}
var numbers = new[] {12, 3, 7, 4, 3, 1, 8, 22, 65, 33, 7, 9, 3, 3};

var evenNumbers = numbers.Where(x => x%2 == 0)
                .Select(x=> $"Number {x} is even.");
foreach (var evenNumber in evenNumbers)
{
    Console.WriteLine(evenNumber);
}

Please execute this code to see what will happen.

Generics

Generic classes encapsulate operations that are not specific to a particular data type.

class Swapper<T>
{
    public void Swap(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
}

T - is a type passed into the class.

This class can be used in a following way:

class Program
{
    class SuperValue
    {
        public string Value { get; set; }
    }
    static void Main(string[] args)
    {
        int x = 12;
        int y = 20;

        SuperValue value1 = new SuperValue { Value = "Hello” };
        SuperValue value2 = new SuperValue { Value = "World" };

        Swapper<int> swapperOne = new Swapper<int>();
        var swapperTwo = new Swapper<SuperValue>();

        swapperOne.Swap(ref x, ref y);
        swapperTwo.Swap(ref value1, ref value2);

        Console.WriteLine($"x = {x}, y = {y}");
        Console.WriteLine($"v1 = {value1.Value}, v2 = {value2.Value}");
        Console.ReadLine();
    }
}

Please run this code to observe a result.

Generic Constraints is the thing designed to assign some requirements for a type that can be passed into the class. By default, a type parameter can be substituted with any type whatsoever. Constraints can be applied to a type parameter to require more specific type arguments:

  • where T : base-class
    // Base-class constraint
  • where T : interface
    // Interface constraint
  • where T : class
    // Reference-type constraint
  • where T : struct
    // Value-type constraint (excludes Nullable types)
  • where T : new()
    // Parameterless constructor constraint
  • where U : T
    // Naked type constraint

Followign code demonstrates how it works.

class Swapper<T> where T:class
{
    public void Swap(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
}

Please notice that T is limited to be only a reference type. If we use this class with value type E.g. int, it’ll throw a compilation error:

Swapper<int> swapperOne = new Swapper<int>();

System.Collections.Generic

The System.Collections.Generic namespace contains interfaces and classes that define generic collections, which allow users to create strongly typed collections that provide better type safety and performance than non-generic strongly typed collections.

Execute followign lines of code to see how collections works.

List<string> names = new List<string>();
names.Add("John");
names.Add("Mike");
names.Add("Alan");

foreach (var name in names)
{
    Console.WriteLine(name);
}
Dictionary<string,string> bouquet = new Dictionary<string, string>();
bouquet.Add("rose", "red");
bouquet.Add("tulip", "yellow");
bouquet.Add("chamomile", "white");
bouquet.Add("aster", "white");

Console.WriteLine($"Rose's color is {bouquet["rose"]}.");

Console.WriteLine("All flowers in bouquet.");
Console.WriteLine($"Total number is {bouquet.Count}");
foreach (var flower in bouquet)
{
    Console.WriteLine($"{flower.Key} is {flower.Value}");
}

Console.WriteLine("White flowers are:");
foreach (var flower in bouquet.Where(x=>x.Value== "white"))
{
    Console.WriteLine($"{flower.Key}");
}

Attributes

Attributes extend classes and types. This C# feature allows you to attach declarative information, like some metadata information, to any type. .NET framework has Attribute class. You should do next steps to have your own attribute: 1) Create and new class with name ended with Attribute. 2) Inherit it from standard Attribute class. 3) Implement any functionality if necessary.

Example:

class DisplayValueAttribute : Attribute
{
    public string Text { get; set; }
}

or

public class HideAttribute : Attribute
{
}

These attributes can be used as:

class Record
{
    [DisplayValue(Text="Name of the record")]
    public string Name { get; set; }

    public string Value { get; set; }

    [DisplayValue(Text = "Created at")]
    public DateTime Created { get; set; }

    [Hide]
    public DateTime Changed { get; set; }
}

At the end of the lesson you can find a demo to see it in action.

Reflection

Reflection objects are used for obtaining type information at runtime. The classes that give access to the metadata of a running program are in the System.Reflection namespace. Reflection is used to fetch attributes information for types.

Reflection examples:

System.Reflection.Assembly info = typeof(int).Assembly;
Console.WriteLine(info);
int i = 42;
Type type = i.GetType();
Console.WriteLine(type);

Exporter demo

Download the source code demo by following this link.

Generics

References

Presentation

Lesson11 from Alex Honcharuk