C# Tutorial
C# String
C# Array
C# Flow Control
C# Class and Object
C# Inheritance
C# Interface
C# Collection
C# Generic
C# File I/O
C# Delegate and Event
C# Exception
C# Process and Thread
C# ADO.NET Database Operations
In this tutorial, we'll cover the basics of using generics in C#. Generics provide a way to create classes, interfaces, and methods with placeholders for the types of their fields, methods, and parameters. This allows you to create more flexible and reusable code.
Imagine you want to create a simple class to store a pair of items. You could create a separate class for each possible type of item, but this would lead to a lot of code duplication. Instead, you can use generics to create a single class that can store any type of item.
Here's an example of a generic class called Pair
that can store two items of any type:
public class Pair<T1, T2> { public T1 First { get; set; } public T2 Second { get; set; } public Pair(T1 first, T2 second) { First = first; Second = second; } }
The angle brackets <T1, T2>
indicate that this is a generic class with two type parameters, T1
and T2
. You can use any valid identifier for the type parameters, but it's common to use single uppercase letters like T
, U
, V
, etc.
To create an instance of the Pair
class, you need to specify the types for T1
and T2
:
var pairOfStringAndInt = new Pair<string, int>("Hello", 42);
You can also create instances of the Pair
class with other types:
var pairOfIntAndDouble = new Pair<int, double>(1, 3.14); var pairOfBoolAndString = new Pair<bool, string>(true, "World");
You can also create generic interfaces:
public interface IRepository<T> { void Add(T item); bool Remove(T item); T Find(Func<T, bool> predicate); IEnumerable<T> GetAll(); }
Here's an example of a simple in-memory implementation of the IRepository
interface:
public class InMemoryRepository<T> : IRepository<T> { private readonly List<T> _items = new List<T>(); public void Add(T item) => _items.Add(item); public bool Remove(T item) => _items.Remove(item); public T Find(Func<T, bool> predicate) => _items.FirstOrDefault(predicate); public IEnumerable<T> GetAll() => _items; }
You can also create generic methods within non-generic classes:
public class Utility { public static T Min<T>(T a, T b) where T : IComparable<T> { return a.CompareTo(b) < 0 ? a : b; } }
In this example, the Min
method accepts two arguments of type T
and returns the minimum of the two. The where T : IComparable<T>
constraint ensures that T
implements the IComparable<T>
interface, allowing the CompareTo
method to be called.
You can use the Min
method like this:
int minValue = Utility.Min(5, 10); string minString = Utility.Min("apple", "banana");
This tutorial only scratches the surface of what you can do with generics in C#. There are many other features, such as generic constraints, co- and contravariant type parameters, and more. Generics are a powerful feature in C# that can
How to use generics in C#:
using System; class Program { static void Main() { // Example of using generics GenericPrinter<int> intPrinter = new GenericPrinter<int>(); intPrinter.Print(42); GenericPrinter<string> stringPrinter = new GenericPrinter<string>(); stringPrinter.Print("Hello, Generics!"); } } class GenericPrinter<T> { public void Print(T value) { Console.WriteLine(value); } }
C# generic class example:
using System; class Box<T> { public T Content { get; set; } } class Program { static void Main() { // Example of generic class Box<int> intBox = new Box<int> { Content = 42 }; Console.WriteLine(intBox.Content); Box<string> stringBox = new Box<string> { Content = "C# Generics" }; Console.WriteLine(stringBox.Content); } }
C# generic method example:
using System; class Program { static void Main() { // Example of generic method int resultInt = GenericMethod<int>(10); string resultString = GenericMethod<string>("C# Generics"); Console.WriteLine(resultInt); Console.WriteLine(resultString); } static T GenericMethod<T>(T input) { return input; } }
Constraints in C# generics:
using System; interface IWritable { void Write(); } class Document : IWritable { public void Write() { Console.WriteLine("Writing document..."); } } class Printer<T> where T : IWritable { public void PrintDocument(T document) { document.Write(); } } class Program { static void Main() { // Example of generic with constraints Printer<Document> documentPrinter = new Printer<Document>(); documentPrinter.PrintDocument(new Document()); } }
Generic collections in C#:
using System; using System.Collections.Generic; class Program { static void Main() { // Example of generic collection List<int> numbers = new List<int> { 1, 2, 3, 4, 5 }; foreach (int number in numbers) { Console.WriteLine(number); } } }
C# generic interfaces:
using System; interface ILogger<T> { void Log(T message); } class ConsoleLogger<T> : ILogger<T> { public void Log(T message) { Console.WriteLine($"Logging: {message}"); } } class Program { static void Main() { // Example of generic interface ILogger<string> stringLogger = new ConsoleLogger<string>(); stringLogger.Log("C# Generics are powerful!"); } }
Using generic delegates in C#:
Action
and Func
allow you to create flexible and type-safe methods.using System; class Program { static void Main() { // Example of generic delegates Action<string> stringAction = Console.WriteLine; stringAction("Hello, Generics!"); Func<int, int, int> add = (a, b) => a + b; Console.WriteLine(add(2, 3)); } }
C# generic class with multiple type parameters:
using System; class Pair<TFirst, TSecond> { public TFirst First { get; set; } public TSecond Second { get; set; } } class Program { static void Main() { // Example of generic class with multiple type parameters Pair<int, string> data = new Pair<int, string> { First = 42, Second = "C#" }; Console.WriteLine($"First: {data.First}, Second: {data.Second}"); } }