Lesson 11
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)
- Generics
- Generic Constraints
- System.Collections.Generic
- Attributes
- Reflection
- Exporter demo
- References
- Presentation
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.