最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

C#接口新特性概览

网站源码admin2浏览0评论

C#接口新特性概览

接口新语法概览

csharp8.0

在C#中,接口不再仅限于定义不带访问修饰符的方法签名。现在,接口可以包含:

  • 带默认实现的方法
  • 私有方法
  • 受保护方法
  • 静态方法
  • 抽象静态方法
  • 虚静态方法
代码语言:javascript代码运行次数:0运行复制
public interface IOverall
{
    // 最普通的方法
    void Foo();

    // 属性
    string Name { get; set; }

    // 索引器
    int this[int index] { get; set; }

    // 事件
    event EventHandler OnNameChanged;

    // 带默认实现的方法
    void Bar() => Console.WriteLine("Bar");

    // 私有方法(需要带默认实现)
    private void NonPublicMethod1() 

    // 受保护方法(需要带默认实现)
    protected void NonPublicMethod2() 

    // 静态方法(需要带默认实现)
    static void StaticMethod() {
        Console.WriteLine("StaticMethod");
    }

    // 抽象静态方法
    static abstract void AbstractStaticMethod();

    // 虚静态方法(需要带默认实现)
    static virtual void VirtualStaticMethod() {
        Console.WriteLine("VirtualStaticMethod");
    }
}

属性和事件在接口中的本质是声明getter/setter以及事件的add/remove,而类中的属性和事件会后台生成对应的私有字段。索引器在接口中声明getter/setter,类中的索引器除非被标记为abstract,否则必须给出默认实现。

代码语言:javascript代码运行次数:0运行复制
public interface IPropertyAndEvent
{
    string Name { get; set; }

    string this[string key] { get; set; }

    event EventHandler? OnNameChanged;
}

public class ClassWithPropertyAndEvent : IPropertyAndEvent
{
    public string Name { get; set; } = "";

    public string this[string key] {
        get => "";
        set {}
    }

    public event EventHandler? OnNameChanged;
}

在C# 8.0中引入了接口中的默认实现,实现了接口的类可以对接口中的方法进行重写。

代码语言:javascript代码运行次数:0运行复制
public interface IFoo1
{
    void Foo() {
        Console.WriteLine($"这是接口{nameof(IFoo1)}中的一个包含默认实现的方法");
    }

    void Bar() {
        Console.WriteLine($"这是接口{nameof(IFoo1)}中的另一个包含默认实现的方法");
    }
}

public interface IFoo2
{
    void Foo() {
        Console.WriteLine($"这是接口{nameof(IFoo2)}中的一个包含默认实现的方法");
    }
}

public class DemoClass1 : IFoo1, IFoo2
{
    public void Bar() {
        Console.WriteLine($"这是{nameof(DemoClass1)}类对于接口中带有默认实现的方法的实现");
    }
}

使用默认方法:

代码语言:javascript代码运行次数:0运行复制
var demo = new DemoClass1();

// 错误用法 - demo.Foo() 这样是无法调用Foo()的,因为DemoClass1中没有Foo方法的实现。
// 必须这样正确用法 - ((IFoo1)demo).Foo();

// demo.Bar() 结果为:这是DemoClass1类对于接口中带有默认实现的方法的实现
// ((IFoo1)demo).Bar() 结果为:这是DemoClass1类对于接口中带有默认实现的方法的实现

非公开方法一般有两个用途:不让类外面的代码使用,或者让类的公开方法来调用。在接口中使用非公开方法一般需要带有默认实现。

接口中的静态方法是属于接口的,而不是属于实现了该接口的某个类的。

代码语言:javascript代码运行次数:0运行复制
interface IStaticMethod
{
    static void StaticMethod() {
        Console.WriteLine("IStaticMethod.Foo");
    }
}

class DemoClass : IStaticMethod 

这样使用是错误的 DemoClass.StaticMethod();,必须使用 IStaticMethod.StaticMethod();。从这个使用上可以看出,这种使用方式局限性很大。作为接口,你没有办法知道你的子类,所以无法实现特定的实现,所以一般是在泛型中使用静态方法。

定义泛型接口,约束T必须实现当前接口:

代码语言:javascript代码运行次数:0运行复制
interface ISerializable<T> where T : ISerializable<T>
{
    static T? Deserialize(string json) => JsonSerializer.Deserialize<T>(json);
}

class Student : ISerializable<Student>
{
    public int Id { get; set; }
    public string Name { get; set; }

    public override string ToString() => $"Id: {Id}, Name: {Name}";
}

var student = ISerializable<Student>.Deserialize("{\"Id\":42,\"Name\":\"Tom\"}");

抽象静态方法允许在接口中声明一个必须在实现类中提供具体实现的静态方法。

代码语言:javascript代码运行次数:0运行复制
interface IAbstractStaticMethod
{
    static abstract string Foo();
}

class DemoClass : IAbstractStaticMethod
{
    public static string Foo() {
        return nameof(DemoClass);
    }
}

调用 DemoClass.Foo();

经典用法包括:

  1. 单例:
代码语言:javascript代码运行次数:0运行复制
interface ISingleton<T> where T : ISingleton<T>
{
    static abstract T Instance { get; }
}

class SingletonClass : ISingleton<SingletonClass>
{
    private static readonly Lazy<SingletonClass> _instanceHolder = new(() => new SingletonClass());
    public static SingletonClass Instance => _instanceHolder.Value;
}
  1. 操作符:
代码语言:javascript代码运行次数:0运行复制
interface IOperators<T> where T : IOperators<T>
{
    static abstract T operator +(T left, T right);
    static abstract T operator -(T left, T right);
}

class MyNumber : IOperators<MyNumber>
{
    public int Value { get; }

    public MyNumber(int value)
    {
        Value = value;
    }

    public static MyNumber operator +(MyNumber left, MyNumber right)
    {
        return new MyNumber(left.Value + right.Value);
    }

    public static MyNumber operator -(MyNumber left, MyNumber right)
    {
        return new MyNumber(left.Value - right.Value);
    }
}
  1. 工厂模式:
代码语言:javascript代码运行次数:0运行复制
interface IFactory<T>
{
    static abstract T Create();
}

class ClassWithFactoryMethod : IFactory<ClassToBeCreated>
{
    private ClassWithFactoryMethod() 
    public static ClassToBeCreated Create()
    {
        return new ClassToBeCreated();
    }
}

静态虚方法提供了一种方式,允许在接口中定义一个方法,该方法可以在类中被重写,但不是强制的。

代码语言:javascript代码运行次数:0运行复制
interface IVirtualStaticMethod
{
    /// <summary>
    /// 一个接口中的静态虚方法(带有默认实现)
    /// </summary>
    static virtual void Foo()
    {
        Console.WriteLine("Foo from interface");
    }
}

class DemoClass : IVirtualStaticMethod
{
    public static void Foo() { // 无法使用 override 关键字
        Console.WriteLine("Foo from class");
    }
}

// 调用
DemoClass.Foo();

// 错误,接口的静态虚方法无法直接通过接口来调用
IVirtualStaticMethod.Foo() // 错误用法

这样看感觉静态虚方法好像并没有什么实际用处,仅仅是给出建议,并不强求实现接口的类必须实现。其实,虽然接口静态虚方法无法直接通过接口来获取,但可以通过泛型标记T来获取。

代码语言:javascript代码运行次数:0运行复制
public interface ITestInterface
{
    static virtual string TestString1() {
        return "接口中的方法";
    }
}

public interface ITestInterfaceGeneric<T> where T : ITestInterfaceGeneric<T>
{
    static virtual string TestString2() {
        return "泛型接口中的方法2";
    }

    // 这样就可以调用到静态虚方法
    static void TestCallGeneric() {
        Console.WriteLine(T.TestString2());
    }
}

public class A : ITestInterface, ITestInterfaceGeneric<A>
{
    public static string TestString2() {
        return "类A中的方法2";
    }
}

public class B : ITestInterface, ITestInterfaceGeneric<B>
{
    public static string TestString1() {
        return "类B中的方法1";
    }
}

static void TestCallInstance<T>(T t) where T : ITestInterface
{
    // 这样也可以调用到静态虚方法
    Console.WriteLine(T.TestString1());
}

// 依次调用
TestCallInstance(new A());
TestCallInstance(new B());
ITestInterfaceGeneric<A>.TestCallGeneric();
ITestInterfaceGeneric<B>.TestCallGeneric();

这些新特性使得接口在C#中变得更加强大和灵活,为开发者提供了更多的设计选择。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2024年10月12日,如有侵权请联系 cloudcommunity@tencent 删除public泛型接口事件c#

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论