目录
1. 定义
2. 接口的特点与规则
3. 接口的实现
3.1单接口实现
3.2多接口实现
4. 接口的作用和用途
1)扩展行为
2)规范行为
3)降低耦合
5. 接口与继承的比较
1)继承
2)接口
6. 接口与抽象类的比较
1)IComparable(比较器,常用)
2)IComparer(比较器)
3)IEnumerable(枚举器,常用)
4)IEnumerator(枚举器)
本篇文章来分享一下C#的接口。从接口的定义、特点与规则、接口的实现、接口与继承的比较、接口与抽象类的比较等来详细学习接口。
1. 定义
在C#中,接口(interface)是用来定义类或结构(struct)所实现的一组公共行为的抽象。接口本质上定义了行为规范,但不实现行为。接口中的所有成员都是抽象的,它们只声明不实现,所有成员默认是public,不能有字段,也不能有实现代码。接口名建议用”I”开头,其后单词首字母大写 ,如IFlyable,IDriveable
public interface IFlyable
{void Fly();//接口中的方法默认是抽象的
}
简而言之,接口是规范,定义一组对外的行为规范,要求它的实现类必须实现接口的所有成员。其中实现接口的方法不能加override。
2. 接口的特点与规则
1)接口中不能包含字段:接口只能包含方法、属性、索引器和事件,不能包含任何字段。
2)所有成员默认是public:接口中的方法、属性、事件等成员默认是public,不能显式指定访问修饰符。接口的成员不能有访问修饰符,也不能使用abstract关键字。
public interface IDriveable
{void Drive();//无需使用abstract,默认即为抽象方法
}
3)接口中只能定义抽象成员:接口的所有成员默认都是抽象的,因此不需要再使用abstract关键字显式声明。
4)接口不能实例化:接口无法直接创建实例,必须通过实现接口的类或结构来创建对象。如,接口 iObj=new 实现类();
3. 接口的实现
3.1单接口实现
实现接口时,类必须实现接口中的所有成员,否则该类会被标记为抽象类。
public class Car : IDriveable
{public void Drive(){Console.WriteLine("Driving...");}
}
3.2多接口实现
C#允许类实现多个接口,接口之间的多继承是允许的。实现类必须实现接口中的所有成员。包括隐式实现与显式实现。
1)隐式实现:最常见的实现方式,直接实现接口中的成员,默认是public。
2)显式实现:通过接口名称显式实现接口成员,且这些成员在实现类中是private的,只有通过接口类型的引用才能调用。
//隐式实现
public class Car : IDriveable, IFlyable
{public void Drive() => Console.WriteLine("Driving...");public void Fly() => Console.WriteLine("Flying...");
}
//显式实现
public class Car : IDriveable, IFlyable
{void IDriveable.Drive() => Console.WriteLine("Driving...");void IFlyable.Fly() => Console.WriteLine("Flying...");
}
=>被称为表达式主体运算符,是一种简洁的语法,用于将方法、属性、索引器等成员的实现压缩为单行代码,适用于那些仅包含单一表达式的成员。
4. 接口的作用和用途
1)扩展行为
接口允许你在不改变已有类的情况下,扩展类的功能。例如,假设有一个Car类,它没有飞行功能,但我们可以通过实现IFlyable接口,给Car类增加飞行的功能,而不必直接修改原类。
2)规范行为
接口允许我们定义一组行为的规范,使得不同类在行为上保持一致。例如,不同种类的交通工具(汽车、飞机、船只等)都可以实现IDriveable接口,这样我们可以用统一的方式对不同的交通工具进行操作。
3)降低耦合
接口允许代码与实现细节解耦,增强代码的可扩展性和可维护性。使用接口可以降低类与类之间的直接依赖,便于未来的扩展和修改。
5. 接口与继承的比较
1)继承
①类继承只能支持单一继承。一个类只能继承自一个父类。
②类继承是继承父类的具体实现,并可能重写某些方法。
//父类(基类)
public class Animal
{public string Name { get; set; }//父类方法public void Eat(){Console.WriteLine($"{Name} is eating.");}//虚方法,允许子类重写public virtual void Speak(){Console.WriteLine($"{Name} makes a sound.");}
}//子类(派生类)
public class Dog : Animal
{public string Breed { get; set; }//重写父类方法public override void Speak(){Console.WriteLine($"{Name} barks.");}
}public class Program
{public static void Main(){Dog dog = new Dog { Name = "Buddy", Breed = "Golden Retriever" };dog.Eat();//继承自父类的方法,输出:Buddy is eating.dog.Speak();//重写后的方法,输出:Buddy is eating.}
}
2)接口
①接口支持多继承,一个类可以实现多个接口。
②接口只定义行为规范,不关心实现细节,避免了类之间紧耦合。
public interface IAnimal
{void Eat();void Sleep();
}
public interface ISwimmable
{void Swim();
}//类实现多个接口
public class Duck : IAnimal, ISwimmable
{public void Eat(){Console.WriteLine("Duck is eating.");}public void Sleep(){Console.WriteLine("Duck is sleeping.");}public void Swim(){Console.WriteLine("Duck is swimming.");}
}public class Program
{public static void Main(){Duck duck = new Duck();duck.Eat();//实现自 IAnimal 接口,输出:Duck is eating.duck.Sleep();//实现自 IAnimal 接口,输出:Duck is sleeping.duck.Swim();//实现自 ISwimmable 接口,输出:Duck is swimming.}
}
特性 | 继承(Class) | 接口(Interface) |
---|---|---|
继承结构 | 单继承:一个类只能继承自一个父类 | 支持多继承:一个类可以实现多个接口 |
实现细节 | 继承父类的实现,子类可以重写父类的方法 | 只定义行为规范,不关心实现细节 |
使用场景 | 用于描述类之间的“是一个”关系(例如:狗是动物) | 用于定义不同类可以共享的行为(例如:可游泳的动物) |
访问修饰符 | 可以使用访问修饰符(public, private等) | 所有成员默认是公共的(public) |
构造函数 | 可以有构造函数 | 接口不能有构造函数 |
总结:
类与类:只能单继承。
类与接口:可以多实现(一个类可以实现多个接口)。
接口与接口:可以多继承(一个接口可以继承多个接口)。
6. 接口与抽象类的比较
1)接口:当需要定义一个行为的规范,并允许多个类实现该行为时,使用接口。例如,IDriveable接口可以被多个类(如Car、Truck)实现。
//定义接口 IDriveable
public interface IDriveable
{void Drive(); // 接口方法,只定义行为的规范,不提供实现
}//类 Car 实现 IDriveable 接口
public class Car : IDriveable
{public void Drive(){Console.WriteLine("The car is driving.");}
}//类 Truck 实现 IDriveable 接口
public class Truck : IDriveable
{public void Drive(){Console.WriteLine("The truck is driving.");}
}public class Program
{public static void Main(){IDriveable car = new Car();car.Drive();//调用 Car 类中的 Drive 方法,输出:The car is driving.IDriveable truck = new Truck();truck.Drive();//调用 Truck 类中的 Drive 方法,输出:The truck is driving.}
}
2)抽象类:当希望定义一些通用的行为,并允许一些方法在子类中被重写时使用抽象类。例如,Animal类可以是抽象类,包含一些通用的行为(如Eat方法),同时定义一些抽象方法(如MakeSound方法)。
//定义抽象类 Animal
public abstract class Animal
{//已实现的通用方法public void Eat(){Console.WriteLine("The animal is eating.");}//抽象方法,要求子类实现public abstract void MakeSound();
}//类 Dog 继承 Animal,并实现 MakeSound 方法
public class Dog : Animal
{public override void MakeSound(){Console.WriteLine("Dog Wangwang.");}
}//类 Cat 继承 Animal,并实现 MakeSound 方法
public class Cat : Animal
{public override void MakeSound(){Console.WriteLine("Cat MiaoMiao.");}
}public class Program
{public static void Main(){Animal dog = new Dog();dog.Eat();//调用父类 Animal 中的 Eat 方法,输出:The animal is eating.dog.MakeSound();//调用子类 Dog 中的 MakeSound 方法,输出:Dog Wangwang.Animal cat = new Cat();cat.Eat();//调用父类 Animal 中的 Eat 方法,输出:The animal is eating.cat.MakeSound();//调用子类 Cat 中的 MakeSound 方法,输出:Cat MiaoMiao.}
}
特性 | 接口(Interface) | 抽象类(Abstract Class) |
---|---|---|
成员实现 | 不能包含实现,所有成员默认为抽象的 | 可以包含实现和抽象方法 |
字段 | 不能包含字段 | 可以包含字段 |
多继承支持 | 一个类可以实现多个接口 | 不支持多继承,一个类只能继承一个抽象类 |
方法实现 | 接口的方法只能声明,不能提供实现 | 抽象类可以有部分实现,也可以有抽象方法 |
构造函数 | 接口不能有构造函数 | 抽象类可以有构造函数 |
访问修饰符 | 接口成员默认是 public,不能使用其他访问修饰符 | 抽象类可以定义不同的访问修饰符 |
使用场景 | 用于定义一组行为规范,多个类可以实现这些行为 | 用于定义类的通用行为,允许部分实现并强制子类实现抽象方法 |
简而言之,接口更多用于行为的抽象,而抽象类则更多用于功能的抽象和实现。想要更详细了解继承和抽象类的有关知识,可以参考【超详细】C#基础-面向对象三大特性:封装、继承
7. 接口在C#中的应用
C#中有很多常用的接口,帮助我们统一不同类的行为。以下是一些常见的接口:
1)IComparable(比较器,常用)
类内继承,通过实现CompareTo方法来自定义对象的比较规则,用于比较对象的大小。
2)IComparer(比较器)
比较器继承,通过实现Compare方法,自定义两个对象的比较规则,常用于排序比较。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace InterfacePro
{//调用端class Program{static void Main(string[] args){Student zs = new Student{Id = 1001,Age = 19,Weight = 140};Student ls = new Student{Id = 1002,Age = 18,Weight = 160};//写法1:自定义对象的比较int a = zs.Age.CompareTo(ls.Age);//写法2:一般做法,自定义的类只选择一个属性去比较,实现比较接口IComparableint b = zs.CompareTo(ls);//写法3:基于写法2,如果还要比较其他属性,因为要遵守开闭原则,此时要实现另一个比较器WeightCompare weightCom = new WeightCompare();int c = weightCom.Compare(zs,ls);}}//定义端 复用class Student:IComparable{public int Id { get; set; }public int Age { get; set; }public int Weight { get; set; }public int CompareTo(object obj){return this.Id.CompareTo((obj as Student).Id);}}class WeightCompare : IComparer{public int Compare(object x, object y){return (x as Student).Weight.CompareTo((y as Student).Weight) ;}}
}
3)IEnumerable(枚举器,常用)
通过实现GetEnumerator方法返回一个迭代器IEnumerator,使类型支持简单迭代(foreach)。
4)IEnumerator(枚举器)
本质是一组yield return的语句,通过MoveNext进行逐段访问,自己可以控制迭代的节奏。
public class MyCollection : IEnumerable
{private int[] items = { 1, 2, 3 };public IEnumerator GetEnumerator(){foreach (var item in items){yield return item;}}
}
好了,本次的分享到这里就结束啦,希望对你有所帮助~