C#接口
作者:追风剑情 发布于:2021-1-29 9:33 分类:C#
接口对一组方法签名进行了统一命名。接口允许定义事件、无参属性、有参属性(C#的索引器)。所有这些东西本质上都是方法,它们只是语法上的简化。不过,接口不能定义任何构造器方法,也不能定义任何实例字段。
虽然CLR允许接口定义静态方法、静态字段、常量和静态构造器,但符合CLS标准的接口绝不允许,因为有的编程语言不能定义或访问它们。事实上,C#禁止接口定义任何一种这样的静态成员。
FCL中的几个接口定义
public interface IDisposable { void Dispose(); } public interface IEnumerable { IEnumerator GetEnumerator(); } public interface IEnumerable<T> : IEnumerable { new IEnumerator<T> GetEnumerator(); } public interface ICollection<T> : IEnumerable<T>, IEnumerable { void Add(T item); void Clear(); Boolean Contains(T item); void CopyTo(T[] array, Int32 arrayIndex); Boolean Remove(T item); Int32 Count { get; } Boolean IsReadOnly { get; } } public interface IComparable<in T> { Int32 CompareTo(T other); }
在CLR看来,接口定义就是类型定义。也就是说,CLR会为接口类型对象定义内部数据结构,同时可通过反射机制来查询接口类型的功能。和类型一样,接口可在文件范围中定义,也可嵌套在另一个类型中。定义接口类型时,可指定你希望在任何可见性/可访问性(public, protected, internal等)。
根据约定,接口类型名称以大写字母I开头,目的是方便在源代码中辨认接口类型。CLR支持泛型接口和接口中的泛型方法。
示例
using System;// Point 从 System.Object 派生,并实现了 IComparable<T> public sealed class Point : IComparable<Point> { private Int32 m_x, m_y; public Point(Int32 x, Int32 y) { m_x = x; m_y = y; }// 该方法为 Point 实现 IComparable<T>.CompareTo() public Int32 CompareTo(Point other) { return Math.Sign(Math.Sqrt(m_x * m_x + m_y * m_y) - Math.Sqrt(other.m_x * other.m_x + other.m_y * other.m_y)); } public override String ToString() { return String.Format("({0}, {1})", m_x, m_y); } } public static class Program { public static void Main() { Point[] points = new Point[] { new Point(3, 3), new Point(1, 2), };// 下面调用由 Point 实现的 IComparable<T>的 CompareTo 方法 if (points[0].CompareTo(points[1]) > 0) { Point tempPoint = points[0]; points[0] = points[1]; points[1] = tempPoint; } Console.WriteLine("Points from closest to (0, 0) to farthest:"); foreach (Point p in points) Console.WriteLine(p); } }
C#编译器要求将实现的接口方法标记为public。CLR要求接口方法标记为virtual。不将方法显式标记为virtual,编译器会将它们标记为virtual和sealed;这会阻止派生类重写接口方法。将方法显式标记为virtual,编译器就会将该方法标记为virtual(并保持它的非密封状态),使派生类能重写它。
派生类不能重写sealed的接口方法。但派生类可重新继承同一个接口,并为接口方法提供自己的实现。在对象上调用接口方法时,调用的是该方法在该对象的类型中的实现。下例对此进行了演示:
示例
using System; public static class Program { public static void Main() {/****************** 第一个例子 ******************/ Base b = new Base();// 用b的类型来调用Dispose,显示; "Base's Dispose" b.Dispose();// 用b的对象的类型来调用Dispose,显示; "Base's Dispose" ((IDisposable)b).Dispose();/****************** 第二个例子 ******************/ Derived d = new Derived();// 用d的类型来调用Dispose,显示; "Derived's Dispose" d.Dispose();// 用d的对象的类型来调用Dispose,显示; "Derived's Dispose" ((IDisposable)d).Dispose();/****************** 第三个例子 ******************/ b = new Derived();// 用b的类型来调用Dispose,显示; "Base's Dispose" b.Dispose();// 用b的对象的类型来调用Dispose,显示; "Derived's Dispose" ((IDisposable)b).Dispose(); }// 这个类派生自Object,它实现了IDisposable internal class Base : IDisposable {// 这个方法隐式密封,不能被重写 public void Dispose() { Console.WriteLine("Base's Dispose"); } }// 这个类派生自Base,它重新实现了IDisposable internal class Derived : Base, IDisposable {// 这个方法不能重写Base的Dispose, // 'new' 表明该方法重新实现了IDisposable的Dispose方法 new public void Dispose() { Console.WriteLine("Derived's Dispose");// 注意,下面这行代码展示了如何调用基类的实现(如果需要的话) // base.Dispose(); } } }
关于调用接口方法的更多探讨
FCL的System.String类型继承了System.Object的方法签名及其实现。此外,String类型还实现了几个接口:IComparable, ICloneable, IConvertible, IEnumerable, IComparable<String>, IEnumerable<Char>和IEquatable<String>。这意味着String类型不需要实现(或重写)其Object基类型提供的方法,但必须实现所有接口声明的方法。
CLR允许定义接口类型的字段、参数或局部变量。使用接口类型的变量可以调用该接口定义的方法。此外,CLR允许调用Object定义的方法,因为所有类都继承了Object的方法。以下代码对此进行了演示:
示例
// s变量引用一个String对象 String s = "Jeffrey";// 可以使用s调用在String,Object,IComparable,ICloneable, // IConvertible,IEnumerable中定义的任何方法 // cloneable变量引用同一个String对象 ICloneable cloneable = s;// 使用cloneable只能调用ICloneable接口声明的 // 任何方法(或Object定义的任何方法) // comparable变量引用同一个String对象 IComparable comparable = s;// 使用comparable只能调用IComparable接口声明的 // 任何方法(或Object定义的任何方法) // enumerable变量引用同一个String对象 // 可在运行时将变量从一个接口转换成另一个,只要 // 对象的类型实现了这两个接口 IEnumerable enumerable = (IEnumerable) comparable;// 使用enumerable只能调用IEnumerable接口声明的 // 任何方法(或Object定义的任何方法)
隐式和显式接口方法实现
显式接口方法实现(Explicit Interface Method Implementation, EIMI)
示例
internal sealed class SimpleType : IDisposable { public void Dispose() { Console.Writeline("Dispose"); } } public sealed class Program { public static void Main() { SimpleType st = new SimpleType();//调用公共Dispose方法实现 st.Dispose();//调用IDisposable的Dispose方法的实现 IDisposable d = st; d.Disapose(); } }
Dispose Dispose
示例
internal sealed class SimpleType : IDisposable { public void Dispose() { Console.Writeline("public Dispose"); }//显示实现接口,不允许指定可访问性(比如public或private)。 //但是,编译器生成方法的元数据时,可访问性会自动设为private, //防止其他代码在使用类的实例时直接调用接口方法。 //只有通过接口类型的变量才能调用接口方法。 void IDisposable.Dispose() { Console.Writeline("IDisposable Dispose"); } } public sealed class Program { public static void Main() { SimpleType st = new SimpleType();//调用公共Dispose方法实现 st.Dispose();//调用IDisposable的Dispose方法的实现 IDisposable d = st; d.Disapose(); } }
public Dispose IDisposable Dispose
泛型接口
示例
using System;// 该类型实现泛型IComparable<T>接口两次 public sealed class Number : IComparable<Int32>, IComparable<String> { private Int32 m_val = 5;// 该方法实现IComparable<Int32>的CompareTo方法 public Int32 CompareTo(Int32 n) { return m_val.CompareTo(n); }// 该方法实现IComparable<String>的CompareTo方法 public Int32 CompareTo(String s) { return m_val.CompareTo(Int32.Parse(s)); } } public static class Program { public static void Main() { Number n = new Number();// 将n中的值和一个Int32(5)比较 IComparable<Int32> cInt32 = n; Int32 result = cInt32.CompareTo(5);// 将n中的值和一个String("5")比较 IComparable<String> cString = n; result = cString.CompareTo("5"); } }
示例——协变与逆变
// 逆变:指可以传递给定类型的子类作为实参(即, T的子类)。 // 泛型接口的逆变需要关键字in实现 public interface IComparable<in T> { int CompareTo(T other); }// 协变:指可以返回给定类型的基类(即, T的基类)。 // 泛型接口的协变需要关键字out实现 public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); } //.NET 4.0之后支持协变与逆变,并且尽量使用此特性以增加接口的灵活性。
泛型和接口约束
• 泛型约束可以实现类型安全
• 传递值类型时减少装箱
示例
// M的类型参数T被约束为只支持同时实现了 // IComparable和IConvertible接口的类型 private static Int32 M<T>(T t) where T : IComparable, IConvertible { }// 这种写法会产生装箱操作 private static Int32 M(IComparable t) { }
C#编译器为接口约束生成特殊IL指令,导致直接在值类型上调用接口方法而不装箱。不用接口约束便没有其他办法让C#编译器生成这些IL指令,如此一来,在值类型上调用接口方法总是发生装箱。一个例外是如果值类型实现了一个接口方法,在值类型的实例上调用这个方法不造成值类型的实例装箱。
实现多个具有相同方法名和签名的接口
示例
public interface IWindow { Object GetMenu(); } public interface IRestaurant { Object GetMenu(); } public sealed class MarioPizzeria : IWindow, IRestaurant { Object IWindow.GetMenu() { } Object IRestaurant.GetMenu() { }//这个GetMenu方法是可选的,与接口无关 public Object GetMenu() { } } MarioPizzeria mp = new MarioPizzeria();//调用GetMenu() mp.GetMenu();//调用IWindow.GetMenu() IWindow window = mp; window.GetMenu();//调用IRestaurant.GetMenu() IRestaurant restaurant = mp; restaurant.GetMenu();
显式实现的接口无法被派生类调用
标签: C#
日历
最新文章
随机文章
热门文章
分类
存档
- 2024年11月(3)
- 2024年10月(5)
- 2024年9月(3)
- 2024年8月(3)
- 2024年7月(11)
- 2024年6月(3)
- 2024年5月(9)
- 2024年4月(10)
- 2024年3月(11)
- 2024年2月(24)
- 2024年1月(12)
- 2023年12月(3)
- 2023年11月(9)
- 2023年10月(7)
- 2023年9月(2)
- 2023年8月(7)
- 2023年7月(9)
- 2023年6月(6)
- 2023年5月(7)
- 2023年4月(11)
- 2023年3月(6)
- 2023年2月(11)
- 2023年1月(8)
- 2022年12月(2)
- 2022年11月(4)
- 2022年10月(10)
- 2022年9月(2)
- 2022年8月(13)
- 2022年7月(7)
- 2022年6月(11)
- 2022年5月(18)
- 2022年4月(29)
- 2022年3月(5)
- 2022年2月(6)
- 2022年1月(8)
- 2021年12月(5)
- 2021年11月(3)
- 2021年10月(4)
- 2021年9月(9)
- 2021年8月(14)
- 2021年7月(8)
- 2021年6月(5)
- 2021年5月(2)
- 2021年4月(3)
- 2021年3月(7)
- 2021年2月(2)
- 2021年1月(8)
- 2020年12月(7)
- 2020年11月(2)
- 2020年10月(6)
- 2020年9月(9)
- 2020年8月(10)
- 2020年7月(9)
- 2020年6月(18)
- 2020年5月(4)
- 2020年4月(25)
- 2020年3月(38)
- 2020年1月(21)
- 2019年12月(13)
- 2019年11月(29)
- 2019年10月(44)
- 2019年9月(17)
- 2019年8月(18)
- 2019年7月(25)
- 2019年6月(25)
- 2019年5月(17)
- 2019年4月(10)
- 2019年3月(36)
- 2019年2月(35)
- 2019年1月(28)
- 2018年12月(30)
- 2018年11月(22)
- 2018年10月(4)
- 2018年9月(7)
- 2018年8月(13)
- 2018年7月(13)
- 2018年6月(6)
- 2018年5月(5)
- 2018年4月(13)
- 2018年3月(5)
- 2018年2月(3)
- 2018年1月(8)
- 2017年12月(35)
- 2017年11月(17)
- 2017年10月(16)
- 2017年9月(17)
- 2017年8月(20)
- 2017年7月(34)
- 2017年6月(17)
- 2017年5月(15)
- 2017年4月(32)
- 2017年3月(8)
- 2017年2月(2)
- 2017年1月(5)
- 2016年12月(14)
- 2016年11月(26)
- 2016年10月(12)
- 2016年9月(25)
- 2016年8月(32)
- 2016年7月(14)
- 2016年6月(21)
- 2016年5月(17)
- 2016年4月(13)
- 2016年3月(8)
- 2016年2月(8)
- 2016年1月(18)
- 2015年12月(13)
- 2015年11月(15)
- 2015年10月(12)
- 2015年9月(18)
- 2015年8月(21)
- 2015年7月(35)
- 2015年6月(13)
- 2015年5月(9)
- 2015年4月(4)
- 2015年3月(5)
- 2015年2月(4)
- 2015年1月(13)
- 2014年12月(7)
- 2014年11月(5)
- 2014年10月(4)
- 2014年9月(8)
- 2014年8月(16)
- 2014年7月(26)
- 2014年6月(22)
- 2014年5月(28)
- 2014年4月(15)
友情链接
- Unity官网
- Unity圣典
- Unity在线手册
- Unity中文手册(圣典)
- Unity官方中文论坛
- Unity游戏蛮牛用户文档
- Unity下载存档
- Unity引擎源码下载
- Unity服务
- Unity Ads
- wiki.unity3d
- Visual Studio Code官网
- SenseAR开发文档
- MSDN
- C# 参考
- C# 编程指南
- .NET Framework类库
- .NET 文档
- .NET 开发
- WPF官方文档
- uLua
- xLua
- SharpZipLib
- Protobuf-net
- Protobuf.js
- OpenSSL
- OPEN CASCADE
- JSON
- MessagePack
- C在线工具
- 游戏蛮牛
- GreenVPN
- 聚合数据
- 热云
- 融云
- 腾讯云
- 腾讯开放平台
- 腾讯游戏服务
- 腾讯游戏开发者平台
- 腾讯课堂
- 微信开放平台
- 腾讯实时音视频
- 腾讯即时通信IM
- 微信公众平台技术文档
- 白鹭引擎官网
- 白鹭引擎开放平台
- 白鹭引擎开发文档
- FairyGUI编辑器
- PureMVC-TypeScript
- 讯飞开放平台
- 亲加通讯云
- Cygwin
- Mono开发者联盟
- Scut游戏服务器引擎
- KBEngine游戏服务器引擎
- Photon游戏服务器引擎
- 码云
- SharpSvn
- 腾讯bugly
- 4399原创平台
- 开源中国
- Firebase
- Firebase-Admob-Unity
- google-services-unity
- Firebase SDK for Unity
- Google-Firebase-SDK
- AppsFlyer SDK
- android-repository
- CQASO
- Facebook开发者平台
- gradle下载
- GradleBuildTool下载
- Android Developers
- Google中国开发者
- AndroidDevTools
- Android社区
- Android开发工具
- Google Play Games Services
- Google商店
- Google APIs for Android
- 金钱豹VPN
- TouchSense SDK
- MakeHuman
- Online RSA Key Converter
- Windows UWP应用
- Visual Studio For Unity
- Open CASCADE Technology
- 慕课网
- 阿里云服务器ECS
- 在线免费文字转语音系统
- AI Studio
- 网云穿
- 百度网盘开放平台
- 迅捷画图
- 菜鸟工具
- [CSDN] 程序员研修院
- 华为人脸识别
- 百度AR导航导览SDK
- 海康威视官网
- 海康开放平台
- 海康SDK下载
- git download
交流QQ群
-
Flash游戏设计: 86184192
Unity游戏设计: 171855449
游戏设计订阅号