C# - 泛型:初学者的友好導入

Hello there, aspiring programmer! Today, we're going to embark on an exciting journey into the world of C# Generics. Don't worry if you're new to programming – I'll be your friendly guide, explaining everything step by step. So, grab a cup of coffee (or your favorite beverage), and let's dive in!

C# - Generics

什麼是泛型?

Imagine you're packing for a trip, but you're not sure what you'll need. Wouldn't it be great if you had a magical suitcase that could hold anything? That's essentially what generics are in C# – they're like flexible containers that can work with different types of data.

Generics allow us to write code that can work with any data type, without having to rewrite the same code for each specific type. It's like having a Swiss Army knife instead of a drawer full of specialized tools!

泛型的特點

Now, let's explore some of the cool features that make generics so useful:

1. 類型安全

Generics ensure that we're using the right type of data in our code. It's like having a smart assistant that prevents us from putting a fish in a birdcage!

2. 代碼重用性

With generics, we can write code once and use it with many different types. It's like having a recipe that works for any kind of fruit pie!

3. 效能

Generics can make our programs run faster because they avoid unnecessary type conversions. Think of it as speaking directly to someone instead of using a translator.

4.靈活性

We can create generic classes, methods, and interfaces that work with any type we choose. It's like having a universal remote control for all your devices!

Let's look at some code examples to see these features in action.

泛型方法

A generic method is like a chef who can cook any dish you request. Let's create a simple generic method:

public static void PrintItem<T>(T item)
{
Console.WriteLine($"The item is: {item}");
}

In this example, <T> is a placeholder for any type we want to use. We can call this method with different types:

PrintItem<int>(42);
PrintItem<string>("Hello, Generics!");
PrintItem<double>(3.14);

Output:

The item is: 42
The item is: Hello, Generics!
The item is: 3.14

Isn't that cool? We wrote one method, but it works with integers, strings, and decimals!

Now, let's create a more practical example – a generic method to find the largest item in an array:

public static T FindLargest<T>(T[] array) where T : IComparable<T>
{
if (array == null || array.Length == 0)
{
throw new ArgumentException("Array cannot be null or empty");
}

T largest = array[0];
for (int i = 1; i < array.Length; i++)
{
if (array[i].CompareTo(largest) > 0)
{
largest = array[i];
}
}
return largest;
}

Let's break this down:

  • <T> is our generic type.
  • where T : IComparable<T> is a constraint that ensures T can be compared.
  • We start with the first element as the largest and compare it with the rest.

Now we can use this method with different types:

int[] numbers = { 5, 3, 8, 2, 9 };
string[] names = { "Alice", "Bob", "Charlie", "David" };

Console.WriteLine($"Largest number: {FindLargest(numbers)}");
Console.WriteLine($"Last name alphabetically: {FindLargest(names)}");

Output:

Largest number: 9
Last name alphabetically: David

泛型類

Generic classes are like multipurpose containers. Let's create a simple generic stack:

public class GenericStack<T>
{
private List<T> items = new List<T>();

public void Push(T item)
{
items.Add(item);
}

public T Pop()
{
if (items.Count == 0)
{
throw new InvalidOperationException("Stack is empty");
}
T item = items[items.Count - 1];
items.RemoveAt(items.Count - 1);
return item;
}

public bool IsEmpty()
{
return items.Count == 0;
}
}

Now we can use this stack with any type:

GenericStack<int> numberStack = new GenericStack<int>();
numberStack.Push(1);
numberStack.Push(2);
numberStack.Push(3);

while (!numberStack.IsEmpty())
{
Console.WriteLine(numberStack.Pop());
}

GenericStack<string> bookStack = new GenericStack<string>();
bookStack.Push("The Great Gatsby");
bookStack.Push("To Kill a Mockingbird");
bookStack.Push("1984");

while (!bookStack.IsEmpty())
{
Console.WriteLine(bookStack.Pop());
}

Output:

3
2
1
1984
To Kill a Mockingbird
The Great Gatsby

泛型委託

Generic delegates are like flexible job descriptions. They allow us to create methods that can work with different types of functions. Here's an example:

public delegate T Operation<T>(T a, T b);

public static T PerformOperation<T>(T a, T b, Operation<T> operation)
{
return operation(a, b);
}

Now we can use this with different operations:

Operation<int> add = (a, b) => a + b;
Operation<int> multiply = (a, b) => a * b;

Console.WriteLine($"5 + 3 = {PerformOperation(5, 3, add)}");
Console.WriteLine($"5 * 3 = {PerformOperation(5, 3, multiply)}");

Operation<string> concatenate = (a, b) => a + b;
Console.WriteLine($"Hello + World = {PerformOperation("Hello ", "World", concatenate)}");

Output:

5 + 3 = 8
5 * 3 = 15
Hello + World = Hello World

結論

Congratulations! You've just taken your first steps into the wonderful world of C# Generics. We've covered generic methods, classes, and delegates, seeing how they can make our code more flexible and reusable.

Remember, learning to use generics effectively is like learning to cook – it takes practice, but once you get the hang of it, you'll be able to create amazing things with minimal effort. Keep experimenting, and don't be afraid to make mistakes. That's how we all learn and grow as programmers.

Happy coding, and may your generics always be flexible and your code always compile on the first try!

泛型概念 描述 示例
泛型方法 可以使用任何數據類型的方法 public static T FindLargest<T>(T[] array) where T : IComparable<T>
泛型類 可以使用任何數據類型實例化的類 public class GenericStack<T>
泛型委託 可以使用不同類型的函數的方法 public delegate T Operation<T>(T a, T b);
類型約束 對可以與泛型一起使用的類型的限制 where T : IComparable<T>
多個類型參數 在泛型中使用多於一個類型參數 public class KeyValuePair<TKey, TValue>

Credits: Image by storyset