C#教程 - 接口类型(Interface Type)

更新记录
转载请注明出处。
2022年9月13日 发布。
2022年9月10日 从笔记迁移到博客。

接口类型(Interface Type)

接口说明#

The interface defines the 'what' part of the syntactical contract and

the deriving classes define the 'how' part of the syntactical contract

接口是指一组 函数成员声明 但不实现方法体 的引用类型

接口成员只可以是函数成员:方法、属性、索引器、事件

只能由 类、结构 来实现接口

注意:接口的方法成员默认是隐式抽象(Implicitly Abstract)

类型命名规范#

I + Pascal风格

[修饰符] interface IFuckable
{

}

声明接口#

接口可以包含:方法、属性、事件、索引器成员

接口不可以包含数据成员、静态成员

interface IInterfaceName
{
    
}

注意:函数成员不包含任何实现代码(方法体),成员声明后必须使用分号

提示:接口名称必须从大写的I开始(比如ISaveable)

提示:接口也可以声明成 分部接口

注意:接口本身声明可以使用任何修饰符,public、protected、internal、private

注意:接口成员默认public abstract,不可以使用访问修饰符

注意:只有类、结构可以实现接口

注意:接口可以多继承

注意:接口不可以包含数据成员、静态成员

注意:接口可以包括:方法、属性、索引器、事件
image

实现接口#

说明:

只有类和结构才能实现接口

类和结构可以实现多个接口

实例:

//接口
interface IPandable
{
    bool IsPanda();
}

//实现
class Panda: IPandable
{
    public bool IsPanda()
    {
        return true;
    }
}

隐式实现#

image

注意:如果有基类要放在接口前面
image

类或结构可以实现多个接口
image

示意图:
image

显式实现#

如果类或结构实现多个接口,并且接口内成员具有重复名称,需要实现显式实现

如果不影响使用,只需要一个实现就可以了,可以通过转换为各种的接口类型,再操作接口的成员

//第一个接口
interface IIfc1
{
    void PrintOut(string s);
}

//第二个接口
interface IIfc2
{
    void PrintOut(string s);
}

class MyClass: IIfc1, IIfc2  //实现两个接口
{
    public void PrintOut(string s)  //两个接口的单一实现
    {
        Console.WriteLine("Just 1 real");
    }
}

示意图:
image

如果需要分别实现不同接口内的相同签名的成员,需要使用显示实现

注意:

​ 显示实现方法格式:接口名.方法名

​ 定义方法不能有修饰符

​ 使用方法只能将对象转为相应的接口再调用

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

namespace Panda
{
    //第一个接口
    interface IIfc1
    {
        void PrintOut(string s);
    }
    
    //第二个接口
    interface IIfc2
    {
        void PrintOut(string s);
    }

    class MyClass: IIfc1, IIfc2  //实现两个接口
    {
        void IIfc1.PrintOut(string s)  //IIfc1显式实现
        {
            Console.WriteLine("IIfc1");
        }

        void IIfc2.PrintOut(string s)  //IIfc2显式实现
        {
            Console.WriteLine("IIfc2");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass a = new MyClass();
            IIfc1 ifc1 = a as IIfc1;
            //调用IIfc1实现
            if(ifc1 != null)
            {
                ifc1.PrintOut("test");
            }

            IIfc2 ifc2 = a as IIfc2;
            //调用IIfc2实现
            if (ifc2 != null)
            {
                ifc2.PrintOut("test");
            }

            Console.ReadKey();
        }
    }
}

示意图:
image

接口的默认实现(Default Interface Members)#

注意:C# 8开始可用

注意:Default implementations are always explicit

注意:使用的时候,必须转为接口才可以使用

注意:还可以在接口的默认实现中定义静态字段

注意:默认成员可以定义属性,但不可以直接初始化

实例:

interface IPandaInterface
{
    void DoSomething(string text) =>
        Console.WriteLine(Prefix + text);

    static string Prefix = "";

    void Dosomething2()
    {
        Console.WriteLine("Abc");
    }

    int this[int x] {
        get 
        {
            return 1; 
        }

        set 
        { 

        } 
    }
}

实例:定义接口的默认实现并使用

using System;
namespace ConsoleApp1
{
    interface IPandaInterface
    {
        public void DoSomething(string text) =>
          Console.WriteLine(Prefix + text);

        static string Prefix = "";

        public void DoSomething2()
        {
            Console.WriteLine("Abc");
        }
        public int this[int x] {
            get 
            {
                return 1; 
            }

            set 
            { 

            } 
        }
    }

    class PandaClass : IPandaInterface
    {

    }
    class Program
    {
        static void Main(string[] args)
        {

            PandaClass pandaClass = new PandaClass();
            //只可以显式使用
            ((IPandaInterface)pandaClass).DoSomething("Panda");
            ((IPandaInterface)pandaClass).DoSomething2();
            //wait
            Console.ReadKey();
        }
    }
}

实例:定义一个带默认方法的接口

interface ILogger
{
 void Log (string text) => Console.WriteLine (text);
}

实例:实现带默认成员的接口

class Logger : ILogger { }
((ILogger)new Logger()).Log ("message");

实例:带静态默认成员接口
interface ILogger
{
    void Log (string text) =>
    Console.WriteLine (Prefix + text);
    static string Prefix = "";
}
ILogger.Prefix = "File log: ";

实例:定义了带初始化的属性,错误

public interface PandaInterface
{
    public int Code { get; set; }
    public int Code2 { get; set; } = 666 //error
}

分部接口(Partial Interface)#

image

接口继承接口#

接口可以继承接口,并且可以多继承

interface IDataRetieve
{
    int GetData();
}

interface IDataStore
{
    void SetData(int value);
}

interface IDataIO: IDataRetieve, IDataStore
{
}

class MyData: IDataIO
{
    public int GetData()
    {
        throw new NotImplementedException();
    }
    public void SetData(int value)
    {
        throw new NotImplementedException();
    }
}

接口继承问题#

父类已经实现了接口的方法#

如果父类实现了某一个方法,而子类继承了某个接口的方法正好和父类的签名相同,则子类可以不用再实现该方法

using System;

namespace Test
{
    interface IIfc1
    {
        void PrintOut(string s);
    }

    //声明基类
    class MyBaseClass
    {
        public void PrintOut(string s)
        {
            Console.WriteLine(s);
        }
    }

    //声明派生类
    class Derived: MyBaseClass, IIfc1
    {

    }

    class Program
    {
        static void Main()
        {
            //调用派生类
            Derived d = new Derived();
            d.PrintOut("test");

            Console.ReadKey();
        }
    }
}

示意图:
image

子类再次实现接口#

如果父类已经实现了接口,子类可以再次实现,则覆盖父类的接口

实例:

using System;

namespace PandaTest
{
    interface PandaInterface1
    {
        void DoSomething();
    }

    class PandaClass1 : PandaInterface1
    {
        public void DoSomething()
        {
            Console.WriteLine("PandaClass1");
        }
    }

    class PandaClass2 : PandaClass1, PandaInterface1
    {
        public new void DoSomething()
        {
            Console.WriteLine("PandaClass2");
        }
    }

    class Program
    {
        
        static void Main()
        {
            PandaClass2 pandaClass2 = new PandaClass2();
            pandaClass2.DoSomething(); //PandaClass2

            //wait
            Console.ReadKey();
        }
    }
}

实例:接口与virtual、override结合使用

public interface IUndoable { void Undo(); }
public class TextBox : IUndoable
{
    public virtual void Undo() => Console.WriteLine ("TextBox.Undo");
}

public class RichTextBox : TextBox
{
    public override void Undo() => Console.WriteLine ("RichTextBox.Undo");
}

RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo
((IUndoable)r).Undo(); // RichTextBox.Undo
((TextBox)r).Undo(); // RichTextBox.Undo

实例:多次实现接口

public interface IUndoable { void Undo(); }
public class TextBox : IUndoable
{
    void IUndoable.Undo() => Console.WriteLine ("TextBox.Undo");
}
public class RichTextBox : TextBox, IUndoable
{
    public void Undo() => Console.WriteLine ("RichTextBox.Undo");
}
RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo
((IUndoable)r).Undo(); // RichTextBox.Undo
((TextBox)r).Undo(); // TextBox.Undo

结构实现接口后转为接口将发生装箱操作#

using System;

namespace PandaTest
{
    public interface PandaInterface1
    {
        void DoSomething();
    }

    public struct PandaStruct : PandaInterface1
    {
        public void DoSomething()
        {
            Console.WriteLine("PandaStruct");
        }
    }

    class Program
    {
        
        static void Main()
        {
            PandaStruct pandaStruct = new PandaStruct();
            //发生装箱操作
            PandaInterface1 pandaInterface1 = pandaStruct;

            //wait
            Console.ReadKey();
        }
    }
}

接口与装箱(Interfaces and Boxing)#

Converting a struct to an interface causes boxing

Calling an implicitly implemented member on a struct does not cause boxing

实例:

interface I { void Foo(); }
struct S : I { public void Foo() {} }
S s = new S();
s.Foo(); // No boxing.
I i = s; // Box occurs when casting to interface.
i.Foo();

接口兼容原则#

如果系统非常庞大,修改接口不方便,更新接口应继承原接口,而不是直接修改接口

DO NOT add abstract members to an interface that has already been published

接口是引用类型#

接口时引用类型,可以将对象转为接口
image

示意图:
image

使用as转换接口类型#

强制将对象转为接口类型,正常情况下没问题的,但如果该对象没有实现该接口,则会抛出异常

使用as运算符进行转换为接口类型,可以避免抛出异常,如果正常转换则返回接口的引用,如果无法转换则返回null
image

抽象类和接口的区别#

抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能

抽象类则偏重于IS-A式的关系,而接口着重于CAN-DO关系类型

抽象类多定义对象的属性,接口多定义对象的行为

在抽象方法声明中不能使用 static 或 virtual 修饰符(C#8可以)

从抽象类派生的非抽象类必须包括继承的所有抽象成员的实现

一个类只能继承一个抽象类,而一个类却可以实现多个接口

类之间的继承侧重类的复用,接口侧重功能复用
image

image

泛型接口#

interface PandaInterface<T1,T2>
{
    void DoSomething(T1 arg1);
    void DoSomething2(T2 arg2);
}

.NET 常见预定义接口#

IComparable CompareTo(other)

IComparer Compare(first, second)

IDisposable Dispose()

IFormattable ToString(format, culture)

IFormatter Serialize(stream,object)

Deserialize(stream)

IFormatProvider GetFormat(type)

实例:使用IComparable接口自定义比较#

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    class Person:IComparable<Person>
    {
        public Person(string name, int age, string code)
        {
            this.Name = name;
            this.Age = age;
            this.Code = code;
        }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Code { get; set; }
        public int CompareTo(Person other)
        {
            if (this.Age <= other.Age)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //array
            Person[] people = new Person[] {
                new Person("Panda",666,"666"),
                new Person("Dog",888,"888"),
                new Person("Cat",778,"778"),
            };

            Array.Sort(people);

            foreach (var item in people)
            {
                Console.WriteLine($"{item.Name} {item.Age} {item.Code}");
            }

            Console.ReadKey();
        }
    }
}

实例:使用IComparer接口自定义比较#

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    class Person
    {
        public Person(string name, int age, string code)
        {
            this.Name = name;
            this.Age = age;
            this.Code = code;
        }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Code { get; set; }
    }

    class PersonCompareByAge : IComparer<Person>
    {
        public int Compare(Person x, Person y)
        {
            if (x.Age <= y.Age)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
    }

    class PersonCompareByCode : IComparer<Person>
    {
        public int Compare(Person x, Person y)
        {
            if (Convert.ToInt32(x.Code) >= Convert.ToInt32(y.Code))
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //array
            Person[] people = new Person[] {
                new Person("Panda",666,"666"),
                new Person("Dog",888,"888"),
                new Person("Cat",778,"778"),
            };

            //Array.Sort(people,new PersonCompareByAge());
            Array.Sort(people, new PersonCompareByCode());

            foreach (var item in people)
            {
                Console.WriteLine($"{item.Name} {item.Age} {item.Code}");
            }

            Console.ReadKey();
        }
    }
}

作者:重庆熊猫

出处:https://www.cnblogs.com/cqpanda/p/16678378.html

版权:本作品采用「不论是否商业使用都不允许转载,否则按3元1字进行收取费用」许可协议进行许可。

posted @   重庆熊猫  阅读(915)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示