 默认接口成员
默认接口成员
  C# 8.0中新增了一个重要特性,就是Default Interface Members(默认接口成员)。默认接口成员允许在接口中定义默认的实现,这使得接口更加灵活,可以避免在实现该接口的类或结构体中重复编写相同的代码。本篇文章将详细介绍C# 8.0中Default Interface Members的各个方面。
# 常量
接口中可以定义常量,这些常量必须是静态的、只读的、且已知的。在C# 8.0中,接口中的常量可以定义默认值,从而成为默认接口成员。在实现该接口的类或结构体中,如果没有重写这些常量,将使用默认值。
public interface IExample
{
    const int DefaultSize = 10;
    const string DefaultName = "Default";
    int Size { get; }
    string Name { get; }
}
public class Example : IExample
{
    public int Size { get; set; } = IExample.DefaultSize;
    public string Name { get; set; } = IExample.DefaultName;
}
var example = new Example();
Console.WriteLine(example.Size); // 输出:10
Console.WriteLine(example.Name); // 输出:"Default"
# 运算符
接口中可以定义运算符,这些运算符可以是二元运算符或一元运算符。在C# 8.0中,接口中的运算符可以定义默认实现,从而成为默认接口成员。在实现该接口的类或结构体中,如果没有重写这些运算符,将使用默认实现。
public interface IExample
{
    static IExample operator +(IExample a, IExample b) => new Example(a.Size + b.Size, a.Name + b.Name);
    int Size { get; }
    string Name { get; }
}
public class Example : IExample
{
    public Example(int size, string name)
    {
        Size = size;
        Name = name;
    }
    public int Size { get; }
    public string Name { get; }
}
var example1 = new Example(10, "A");
var example2 = new Example(20, "B");
var example3 = example1 + example2;
Console.WriteLine(example3.Size); // 输出:30
Console.WriteLine(example3.Name); // 输出:"AB"
# 静态构造函数
接口中可以定义静态构造函数。在C# 8.0中,接口中的静态构造函数可以定义默认实现,从而成为默认接口成员。在实现该接口的类或结构体中,如果没有重写这个静态构造函数,将使用默认实现。
public interface IExample
{
    static int MaxSize { get; }
    static string DefaultName { get; }
    static IExample DefaultInstance { get; }
    static IExample()
    {
        MaxSize = 100;
        DefaultName = "Default";
        DefaultInstance = new Example(MaxSize, DefaultName);
    }
    int Size { get; }
    string Name { get; }
}
public class Example
{
    public int Size { get; }
    public string Name { get; }
    public Example(int size, string name)
    {
        Size = size;
        Name = name;
    }
}
var example1 = new Example(50, "A");
var example2 = IExample.DefaultInstance;
Console.WriteLine(example1.Size); // 输出:50
Console.WriteLine(example1.Name); // 输出:"A"
Console.WriteLine(example2.Size); // 输出:100
Console.WriteLine(example2.Name); // 输出:"Default"
# 嵌套类型
接口中可以定义嵌套类型,这些嵌套类型可以是类、结构体、枚举或接口。在C# 8.0中,嵌套类型可以定义默认接口成员,这些默认接口成员将被嵌套类型的所有实现继承。
public interface IExample
{
    public class NestedClass
    {
        public virtual string GetName() => "Default";
    }
    int Size { get; }
    string Name { get; }
}
public class Example : IExample
{
    public int Size { get; set; }
    public string Name { get; set; }
    public class NestedClass : IExample.NestedClass
    {
        public override string GetName() => "Custom";
    }
}
var example = new Example();
var nested = new Example.NestedClass();
Console.WriteLine(nested.GetName()); // 输出:"Custom"
# 静态字段、方法、属性、索引器和事件
接口中可以定义静态字段、方法、属性、索引器和事件。在C# 8.0中,这些静态成员可以定义默认接口实现,从而成为默认接口成员。在实现该接口的类或结构体中,如果没有重写这些静态成员,将使用默认实现。
public interface IExample
{
    static int MaxSize { get; }
    static string DefaultName { get; }
    static IExample DefaultInstance { get; }
    static IExample()
    {
        MaxSize = 100;
        DefaultName = "Default";
        DefaultInstance = new Example(MaxSize, DefaultName);
    }
    static int GetSize() => MaxSize;
    static string GetName() => DefaultName;
    static event EventHandler MyEvent;
    int Size { get; }
    string Name { get; }
}
public class Example : IExample
{
    public int Size { get; }
    public string Name { get; }
    public Example(int size, string name)
    {
        Size = size;
        Name = name;
    }
    static int GetSize() => 50;
    static string GetName() => "Custom";
}
Console.WriteLine(IExample.GetSize()); // 输出:100
Console.WriteLine(IExample.GetName()); // 输出:"Default"
IExample.MyEvent += (sender, e) => Console.WriteLine("Event raised.");
IExample.MyEvent?.Invoke(null, EventArgs.Empty); // 输出:"Event raised."
# 显式接口实现
接口中可以定义显式接口实现,即为接口成员定义一个明确的实现,只能通过接口访问这个成员。在C# 8.0中,显式接口实现可以定义默认实现,从而成为默认接口成员。在实现该接口的类或结构体中,如果没有重写这些显式接口实现,将使用默认实现。
public interface IExample
{
    void Print();
    int Size { get; }
    string Name { get; }
}
public class Example : IExample
{
    public int Size { get; }
    public string Name { get; }
    public Example(int size, string name)
    {
        Size = size;
        Name = name;
    }
    void IExample.Print() => Console.WriteLine("Default");
    public void PrintCustom() => Console.WriteLine("Custom");
}
var example = new Example(10, "A");
example.Print(); // 输出:"Default"
((IExample)example).Print(); // 输出:"Default"
example.PrintCustom(); // 输出:"Custom"
# 访问修饰符
在C#中,默认接口成员的访问修饰符是public,如果需要使用其他访问修饰符,需要显式指定。
public interface IExample
{
    static int MaxSize { get; }
    static string DefaultName { get; }
    static IExample DefaultInstance { get; }
    static IExample()
    {
        MaxSize = 100;
        DefaultName = "Default";
        DefaultInstance = new Example(MaxSize, DefaultName);
    }
    private static int GetSize() => MaxSize;
    protected static string GetName() => DefaultName;
    internal static event EventHandler MyEvent;
    int Size { get; }
    string Name { get; }
}
public class Example : IExample
{
    public int Size { get; }
    public string Name { get; }
    public Example(int size, string name)
    {
        Size = size;
        Name = name;
    }
    protected static string GetName() => "Custom";
}
// 访问默认接口成员,使用public访问修饰符
Console.WriteLine(IExample.MaxSize); // 输出:100
Console.WriteLine(IExample.DefaultName); // 输出:"Default"
IExample.MyEvent += (sender, e) => Console.WriteLine("Event raised.");
IExample.MyEvent?.Invoke(null, EventArgs.Empty); // 输出:"Event raised."
// 访问指定访问修饰符的默认接口成员
// 使用private访问修饰符
Console.WriteLine(typeof(IExample).GetMethod("GetSize", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null)); // 输出:100
// 使用protected访问修饰符
Console.WriteLine(typeof(IExample).GetMethod("GetName", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null)); // 输出:"Default"
// 使用internal访问修饰符
Console.WriteLine(typeof(IExample).GetEvent("MyEvent", BindingFlags.NonPublic | BindingFlags.Static).AddMethod.IsAssembly); // 输出:True
# 结论
C# 8.0中的默认接口成员是一个强大的特性,它使得接口更加灵活、可扩展。通过定义默认接口成员,我们可以避免在实现接口的类或结构体中重复编写相同的代码,提高代码的可维护性和可读性。我们可以在接口中定义常量、运算符、静态构造函数、嵌套类型、静态成员和显式接口实现,并指定相应的访问修饰符。希望这篇文章能够帮助您理解C# 8.0中默认接口成员的各个方面,以便您在编写代码时更加灵活和高效地使用它们。
