0Pricing
C# Academy · 课时

泛型接口

对类型进行抽象。

泛型接口 是 CoddyKit 上的免费 C# Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。

什么是泛型接口?

泛型接口会在名称后的尖括号中声明类型参数。实现类选择具体类型,接口契约也会相应适配。

.NET 类库处处使用泛型接口,例如 IEnumerable<T> 和 IComparable<T>。

interface IRepository<T>
{
    void Add(T item);
    T Get(int id);
}

实现泛型接口

类可以通过提供具体类型参数来实现泛型接口。方法签名随后会使用该实际类型。

这样可以在无需类型转换的情况下,提供类型安全且可复用的契约。

using System.Collections.Generic;

class IntStore : IRepository<int>
{
    private readonly List<int> data = new();
    public void Add(int item) => data.Add(item);
    public int Get(int id) => data[id];
}

interface IRepository<T>
{
    void Add(T item);
    T Get(int id);
}

让实现类保持泛型

类可以在自身保持为泛型类的同时实现泛型接口,将自己的类型参数传递给该接口。

这样可以让实现适用于任意元素类型。

using System.Collections.Generic;

class Store<T> : IRepository<T>
{
    private readonly List<T> data = new();
    public void Add(T item) => data.Add(item);
    public T Get(int id) => data[id];
}

interface IRepository<T> { void Add(T item); T Get(int id); }

实际使用 IComparable<T>

IComparable<T> 是内置的泛型接口。实现它可以让您的类型定义实例的排序方式,从而支持排序。

CompareTo 方法会返回负数、零或正数。

class Money : IComparable<Money>
{
    public int Cents;
    public int CompareTo(Money? other)
        => Cents.CompareTo(other?.Cents ?? 0);
}

实现 IEnumerable<T>

IEnumerable<T> 是 LINQ 和 foreach 的核心。实现该接口的类型可以进行迭代。通常,您可以委托给内部集合的迭代器。

使用带有 yield return 的迭代器方法可以轻松实现这一点。

using System.Collections;
using System.Collections.Generic;

class Bag<T> : IEnumerable<T>
{
    private readonly List<T> items = new();
    public void Add(T x) => items.Add(x);
    public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

可运行的泛型接口

这个完整程序定义了泛型 IPrinter<T>,为字符串实现它,然后使用该实现。

无论具体类型是什么,该接口都保证存在 Print 方法。

using System;

interface IPrinter<T> { void Print(T value); }

class ConsolePrinter<T> : IPrinter<T>
{
    public void Print(T value) => Console.WriteLine(value);
}

class Program
{
    static void Main()
    {
        IPrinter<string> p = new ConsolePrinter<string>();
        p.Print("hello generics");
    }
}

使用 out 的协变

标记为 out 的泛型接口参数具有协变性:它只能出现在输出位置。这样,在需要 IEnumerable<Animal> 的地方就可以使用 IEnumerable<Cat>。

协变性沿继承链保持赋值兼容性。

interface IProducer<out T>
{
    T Produce();
}

使用 in 的逆变

标记为 in 的参数具有逆变性:它只能出现在输入位置。这样,在需要 IComparer<Cat> 的地方就可以使用 IComparer<Animal>。

逆变性会反转消费者的可赋值方向。

interface IConsumer<in T>
{
    void Consume(T item);
}

多个类型参数

泛型接口可以接受多个参数。IDictionary<TKey, TValue> 是一个熟悉的示例,其中键类型和值类型彼此独立。

每个参数都可以独立地是不变、协变或逆变的。

interface IMapper<TIn, TOut>
{
    TOut Map(TIn input);
}

面向接口编程

依赖泛型接口而不是具体类,可以让代码更加灵活。无需修改调用方即可替换实现。

类型为 IEnumerable<T> 的方法参数可以同样接受列表、数组和查询结果。

using System.Collections.Generic;
using System.Linq;

int Total(IEnumerable<int> values) => values.Sum();

使用泛型接口的约束

泛型接口经常用作约束。where T : IComparable<T> 可以让方法对输入进行排序或比较。

这将泛型的两项特性结合起来,从而构建安全且可复用的算法。

T Min<T>(T a, T b) where T : IComparable<T>
    => a.CompareTo(b) <= 0 ? a : b;

快速检查

测试您对泛型接口的理解。

回顾

泛型接口声明类型参数,并允许实现者确定这些参数或将其继续传递。内置示例包括 IEnumerable<T>、IComparable<T> 和 IDictionary<TKey, TValue>。

方差注释 out 和 in 支持协变和逆变,泛型接口也可以自然地与 where 约束结合使用。

常见问题解答

「泛型接口」课时是免费的吗?

是的 — 「泛型接口」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。

「泛型接口」这节课中我会学到什么?

对类型进行抽象。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C# Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「泛型接口」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C# Academy 课中编写并运行代码吗?

能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 泛型方法
  2. 泛型类
  3. where 约束
  4. 泛型接口
← 返回 C# Academy