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,不可以使用访问修饰符
注意:只有类、结构可以实现接口
注意:接口可以多继承
注意:接口不可以包含数据成员、静态成员
实现接口#
说明:
只有类和结构才能实现接口
类和结构可以实现多个接口
实例:
//接口
interface IPandable
{
bool IsPanda();
}
//实现
class Panda: IPandable
{
public bool IsPanda()
{
return true;
}
}
隐式实现#
显式实现#
如果类或结构实现多个接口,并且接口内成员具有重复名称,需要实现显式实现
如果不影响使用,只需要一个实现就可以了,可以通过转换为各种的接口类型,再操作接口的成员
//第一个接口
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");
}
}
如果需要分别实现不同接口内的相同签名的成员,需要使用显示实现
注意:
显示实现方法格式:接口名.方法名
定义方法不能有修饰符
使用方法只能将对象转为相应的接口再调用
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();
}
}
}
接口的默认实现(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)#
接口继承接口#
接口可以继承接口,并且可以多继承
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();
}
}
}
子类再次实现接口#
如果父类已经实现了接口,子类可以再次实现,则覆盖父类的接口
实例:
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
接口是引用类型#
使用as转换接口类型#
强制将对象转为接口类型,正常情况下没问题的,但如果该对象没有实现该接口,则会抛出异常
使用as运算符进行转换为接口类型,可以避免抛出异常,如果正常转换则返回接口的引用,如果无法转换则返回null
抽象类和接口的区别#
抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能
抽象类则偏重于IS-A式的关系,而接口着重于CAN-DO关系类型
抽象类多定义对象的属性,接口多定义对象的行为
在抽象方法声明中不能使用 static 或 virtual 修饰符(C#8可以)
从抽象类派生的非抽象类必须包括继承的所有抽象成员的实现
一个类只能继承一个抽象类,而一个类却可以实现多个接口
泛型接口#
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字进行收取费用」许可协议进行许可。
本文来自博客园,作者:重庆熊猫,转载请注明原文链接:https://www.cnblogs.com/cqpanda/p/16678378.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验