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

2022/9/14 1:19:28

本文主要是介绍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();
        }
    }
}


这篇关于C#教程 - 接口类型(Interface Type)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程