C#中的接口、枚举和结构体

接口

在C#中,接口是一种定义契约的引用类型。它只包含方法、属性、事件或索引器的声明,不包含实现。

从C#8.0开始,接口可以包含默认是实现,比如静态或实例方法,但核心依然是“定义能力,而非细节”。

特性

接口内不能包含实例字段,如int age;

内部没有构造和析构方法,不可以实例化;

一个类可以实现多个接口;

C#8.0前,所有成员默认为public;在之后可以指定private、protected、static、virtual等;

当多个接口有同名方法时,可以显式指定实现哪个。

// 定义接口 public interface IAnimal { // 属性(自动属性在接口中只表示必须存在 get/set) string Name { get; set; } // 方法(无实现体) void MakeSound(); } // 实现接口 public class Dog : IAnimal { public string Name { get; set; } public void MakeSound() { Console.WriteLine($"{Name} says: Woof!"); } }

接口的多实现与显式实现

public interface ICanFly { void Move(); } public interface ICanSwim { void Move(); } public class Duck : ICanFly, ICanSwim { // 显式实现(调用时需转型为对应接口) void ICanFly.Move() => Console.WriteLine("Fly"); void ICanSwim.Move() => Console.WriteLine("Swim"); // 普通实现(会同时满足两个接口,但若重名会冲突,通常用显式) } // 使用 Duck d = new Duck(); ((ICanFly)d).Move(); // 输出 Fly ((ICanSwim)d).Move(); // 输出 Swim

C#8.0后,允许接口中添加带实现的方法

public interface ILogger { void Log(string message); // 默认实现(C# 8.0+) void LogError(string error) { Log($"ERROR: {error}"); } } public class ConsoleLogger : ILogger { public void Log(string message) => Console.WriteLine(message); // 无需实现 LogError,直接继承默认实现 }

枚举

在C#中,枚举(enum)是用于定义一组命名常量的值类型。它让代码更易读、更安全,避免使用魔法数字或字符串。

特性

继承自System.Enum,基类是System.ValueType;

默认值类型为int,可以指定为byte、short、long等整数类型;

每个成员都对应一个整数值,默认从0开始依次递增;

不同枚举之间是不能隐式转换的;

支持位运算。

// 定义枚举(默认从 0 开始) public enum Weekday { Sunday, // 0 Monday, // 1 Tuesday, // 2 Wednesday, // 3 Thursday, // 4 Friday, // 5 Saturday // 6 } // 手动指定值 public enum ErrorCode { Success = 0, NotFound = 404, ServerError = 500, Timeout = 408 // 可以跳号 } // 指定底层类型 public enum FileSize : long { Small = 1024, Large = 1048576, // 如果值超过 int 范围,必须指定 long }

结构体

C#中结构体是一种值类型,用于封装小型数据组。与类不同,结构体在栈上分配,赋值时复制值而非引用。

特性

继承自System.ValueType,赋值即复制;

由于是在栈上分配内存,所以结构体的性能更高,但当占用内存过大时复制开销也大;

结构体是隐式密封,也就是sealed类型,不能继承也不能被继承,但可以实现接口;

内部必须有有参构造函数,在C#10前,无参构造函数不允许被自定义;

在C#10前,字段不能再声明时初始化。

// 定义结构体 public struct Point { // 字段(推荐私有,通过属性访问) private int _x; private int _y; // 属性(可自动属性) public int X { get; set; } public int Y { get; set; } // 构造函数(必须有参数) public Point(int x, int y) { X = x; Y = y; } // 方法 public double DistanceTo(Point other) { return Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2)); } // 重写 ToString public override string ToString() => $"({X}, {Y})"; } // 使用 Point p1 = new Point(3, 4); Point p2 = p1; // 复制值,修改 p2 不影响 p1 p2.X = 10; Console.WriteLine(p1); // (3, 4) Console.WriteLine(p2); // (10, 4)

用readonly修饰,强制不可变性,可以提高安全性

public readonly struct ReadOnlyPoint { public int X { get; } // 只读属性 public int Y { get; } public ReadOnlyPoint(int x, int y) { X = x; Y = y; } // 方法必须是 readonly(不会修改状态) public double DistanceTo(ReadOnlyPoint other) { return Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2)); } }

如果是ref struct,只能分配在栈上,不能装箱也不能作为类字段,用于高性能场景的时候

public ref struct Buffer { private Span<byte> _data; public Buffer(Span<byte> data) { _data = data; } public void Fill(byte value) { _data.Fill(value); } } // 注意:ref struct 不能实现接口,不能装箱