interface(C#参考)
接口定义协定。 实现该协定的任何 class
、record
或 struct
必须提供接口中定义的成员的实现。 接口可为成员定义默认实现。 它还可以定义 static
成员,以便提供常见功能的单个实现。 从 C# 11 开始,接口可以定义 static abstract
或 static virtual
成员来声明实现类型必须提供声明的成员。 通常,static virtual
方法声明实现必须定义一组重载运算符。
在以下示例中,类 ImplementationClass
必须实现一个不含参数但返回 void
的名为 SampleMethod
的方法。
有关详细信息和示例,请参阅接口。
顶级接口(在命名空间中声明但未嵌套在另一种类型的接口中)可以声明为 public
或 internal
。 默认为 internal
。 嵌套接口声明(在另一类型内部声明的接口)可以使用任何访问修饰符声明。
没有实现的接口成员不能包含访问修饰符。 具有默认实现的成员可以包含任何访问修饰符。
接口示例
interface ISampleInterface
{
void SampleMethod();
}
class ImplementationClass : ISampleInterface
{
// Explicit interface member implementation:
void ISampleInterface.SampleMethod()
{
// Method implementation.
}
static void Main()
{
// Declare an interface instance.
ISampleInterface obj = new ImplementationClass();
// Call the member.
obj.SampleMethod();
}
}
接口可以是命名空间或类的成员。 接口声明可以包含以下成员的声明(没有任何实现的签名):
默认接口成员
上述成员声明通常不包含主体。 接口成员可以声明主体。 接口中的成员主体是默认实现。 具有主体的成员允许接口为不提供重写实现的类和结构提供“默认”实现。
重要
添加默认接口成员会强制实现接口的任何 ref struct
添加该成员的显式声明。
接口可能包括:
- 常量
- 运算符
- 静态构造函数。
- 嵌套类型
- 静态字段、方法、属性、索引和事件
- 使用显式接口实现语法的成员声明。
- 显式访问修饰符(默认访问权限为
public
)。
静态抽象成员和虚拟成员
从 C# 11 开始,接口可以声明除字段之外的所有成员类型的 static abstract
和 static virtual
成员。 接口可以声明实现类型必须定义运算符或其他静态成员。 此功能使泛型算法能够指定类似于数字的行为。 可以在 .NET 运行时的数值类型中看到示例,如 System.Numerics.INumber<TSelf>。 这些接口定义由许多数值类型实现的常见数学运算符。 编译器必须在编译时解析对 static virtual
和 static abstract
方法的调用。 接口中声明的 static virtual
和static abstract
方法没有类似于类中声明的 virtual
或 abstract
方法的运行时调度机制。 相反,编译器使用编译时可用的类型信息。 因此,static virtual
方法几乎完全是在泛型接口中声明的。 此外,声明 static virtual
或 static abstract
方法的大多数接口都声明了其中一个类型参数必须实现已声明的接口。 例如,INumber<T>
接口声明 T
必须实现 INumber<T>
。 编译器使用类型参数解析对接口声明中声明的方法和运算符的调用。 例如,int
类型实现 INumber<int>
。 当类型参数 T
表示类型参数 int
时,将调用 int
上声明的 static
成员。 或者,当 double
是类型参数时,将调用在 double
类型上声明的 static
成员。
重要
使用表达式的编译时类型解析接口中声明的 static abstract
和 static virtual
方法的方法调度。 如果表达式的运行时类型派生自不同的编译时类型,将调用基(编译时)类型的静态方法。
可以通过使用有关接口中静态抽象成员的教程来尝试此功能。
接口继承
接口不能包含实例状态。 虽然现在允许使用静态字段,但接口中不允许使用实例字段。 接口中不支持实例自动属性,因为它们将隐式声明隐藏的字段。 此规则对属性声明有细微影响。 在接口声明中,以下代码不会像在接口struct
声明中那样声明自动实现的属性class
。 相反,它会声明一个属性,该属性没有默认实现,而必须在该实现接口的任何类型中实现它:
public interface INamed
{
public string Name {get; set;}
}
一个接口可从一个或多个基接口继承。 当接口继承自另一个接口时,实现派生接口的类型必须实现基接口中的所有成员以及派生接口中声明的成员,如以下代码所示:
public interface I1
{
void M1();
}
public interface I2 : I1
{
void M2();
}
public class C : I2
{
// implements I1.M1
public void M1() { }
// implements I2.M2
public void M2() { }
}
当接口 重写基接口中的方法实现时,必须使用显式接口实现语法。
基类型列表包含基类和接口时,基类必须是列表中的第 1 项。
实现接口的类可以显式实现该接口的成员。 显式实现的成员不能通过类实例访问,而只能通过接口实例访问。 此外,只能通过接口实例访问默认接口成员。
有关显式接口实现的详细信息,请参阅显式接口实现。
接口实现示例
下例演示了接口实现。 在此示例中,接口包含属性声明,类包含实现。 实现 IPoint
的类的任何实例都具有整数属性 x
和 y
。
interface IPoint
{
// Property signatures:
int X { get; set; }
int Y { get; set; }
double Distance { get; }
}
class Point : IPoint
{
// Constructor:
public Point(int x, int y)
{
X = x;
Y = y;
}
// Property implementation:
public int X { get; set; }
public int Y { get; set; }
// Property implementation
public double Distance =>
Math.Sqrt(X * X + Y * Y);
}
class MainClass
{
static void PrintPoint(IPoint p)
{
Console.WriteLine("x={0}, y={1}", p.X, p.Y);
}
static void Main()
{
IPoint p = new Point(2, 3);
Console.Write("My Point: ");
PrintPoint(p);
}
}
// Output: My Point: x=2, y=3
C# 语言规范
有关详细信息,请参阅 C# 语言规范的接口部分、C# 8 - 默认接口成员的功能规范和 C# 11 - 接口中的静态抽象成员的功能规范