c#中的约束
在C#中,约束(Constraints)用于限制泛型类型参数的类型,以确保泛型类型或方法在编译时能够满足特定的要求。约束允许开发者指定泛型类型参数必须满足的条件,比如实现特定的接口或继承自特定的类。以下是一些常见的约束类型:
-
类类型约束 (
class
或struct
):-
class
约束指定类型参数必须是引用类型。 -
struct
约束指定类型参数必须是值类型。
cspublic class MyClass<T> where T : class { // T 必须是引用类型 }
-
-
接口约束 (
interface
):- 接口约束指定类型参数必须实现一个或多个特定的接口。
cspublic interface IInterfaceA { } public interface IInterfaceB { } public class MyGenericClass<T> where T : IInterfaceA, IInterfaceB { // T 必须同时实现 IInterfaceA 和 IInterfaceB }
-
构造函数约束 (
new()
):- 构造函数约束指定类型参数必须有一个无参数的公共构造函数。
cspublic class MyGenericClass<T> where T : new() { public T CreateInstance() => new T(); // T 必须有一个无参数的公共构造函数 }
-
基类约束 (
: BaseType
):- 基类约束指定类型参数必须是指定基类的子类。
cspublic class MyBaseClass { } public class MyGenericClass<T> where T : MyBaseClass { // T 必须是 MyBaseClass 的子类 }
-
多个约束:
- 可以对类型参数应用多个约束,它们可以组合使用。
cspublic class MyGenericClass<T> where T : class, IInterfaceA, new() { // T 必须是引用类型,实现 IInterfaceA 接口,并有一个无参数的公共构造函数 }
-
类型参数默认值约束 (
default
):- 从C# 8.0开始,可以使用
default
约束来指定类型参数必须有一个可用的默认实现。
cspublic class MyGenericClass<T> where T : IInterfaceA = default(T) { // T 必须实现 IInterfaceA 接口,并且有一个可用的默认实现 }
- 从C# 8.0开始,可以使用
TimeSpan
在C#中,TimeSpan
是一个结构体(struct
),用于表示两个 DateTime
对象之间的时间差,或者表示一个持续时间。TimeSpan
结构体包含了时间的天、小时、分钟、秒和毫秒部分。
TimeSpan
提供了多种方法来创建和操作时间间隔,例如:
-
TimeSpan.FromSeconds(double)
:根据秒数创建TimeSpan
。 -
TimeSpan.FromMinutes(double)
:根据分钟数创建TimeSpan
。 -
TimeSpan.FromHours(double)
:根据小时数创建TimeSpan
。 -
TimeSpan.FromDays(double)
:根据天数创建TimeSpan
。
此外,TimeSpan
也支持算术运算,如加法和减法,以及与其他 TimeSpan
结构体的比较。
这里是一个简单的使用 TimeSpan
的示例:
cs
// 创建一个表示2小时30分钟的时间间隔
TimeSpan interval = new TimeSpan(2, 30, 0);
// 创建一个表示15秒的时间间隔
TimeSpan shortInterval = TimeSpan.FromSeconds(15);
// 计算两个DateTime之间的时间差
DateTime start = new DateTime(2024, 1, 1);
DateTime end = new DateTime(2024, 1, 2);
TimeSpan duration = end - start;
TimeSpan
结构体是不可变的,这意味着一旦创建了一个 TimeSpan
实例,它的值就不能被修改。任何修改操作都会返回一个新的 TimeSpan
实例。
c#中的defult
在C#中,default
关键字用于获取一个类型的默认值。每种类型的默认值定义如下:
-
对于值类型(例如
int
,double
,struct
),默认值是将所有位都设置为0。例如,int
的默认值是0
,double
的默认值是0.0
。 -
对于引用类型(例如
class
,string
),默认值是null
。 -
对于
bool
类型,没有默认值,因为bool
是一个值类型,并且它只有两个可能的值:true
和false
。 -
对于
Nullable<T>
类型(可以为 null 的类型),默认值是null
。
使用 default
关键字可以提高代码的可读性和可维护性,特别是当你需要初始化一个变量到其类型的默认值时。例如:
cs
int number = default(int); // 初始化为 0
string text = default(string); // 初始化为 null
double value = default(double); // 初始化为 0.0
此外,default
也可以用于泛型类型参数,编译器会根据类型参数推断出正确的默认值:
cs
List<T> list = new List<T>();
T item = default(T); // 根据 T 的类型,编译器推断出默认值
default
关键字是一个表达式,它在编译时确定类型,并在运行时返回该类型的默认值。
c#中operator
在C#中,operator
关键字用于定义和重载运算符,使得自定义类型可以像内置类型一样使用标准的运算符,如 +
, -
, ==
, !=
等。这提高了代码的可读性和一致性。
以下是一些常见的运算符重载示例:
-
一元运算符 :如
+
,-
,!
,++
,--
。cspublic struct Point { public int X { get; set; } public int Y { get; set; } public static Point operator +(Point p1, Point p2) { return new Point { X = p1.X + p2.X, Y = p1.Y + p2.Y }; } public static Point operator -(Point p) { return new Point { X = -p.X, Y = -p.Y }; } }
-
二元运算符 :如
+
,-
,*
,/
,%
,==
,!=
。cspublic static bool operator ==(Point p1, Point p2) { return p1.X == p2.X && p1.Y == p2.Y; } public static bool operator !=(Point p1, Point p2) { return !(p1 == p2); }
-
递增和递减运算符 :
++
和--
。cspublic class Counter { private int _value; public int Value { get { return _value; } set { _value = value; } } public static Counter operator ++(Counter c) { c._value++; return c; } public static Counter operator --(Counter c) { c._value--; return c; } }
-
关系运算符 :
<
,>
,<=
,>=
。cspublic class Fraction { private int _numerator; private int _denominator; public static bool operator <(Fraction f1, Fraction f2) { // 实现比较逻辑 } public static bool operator >(Fraction f1, Fraction f2) { // 实现比较逻辑 } }
-
赋值运算符 :
=
。cspublic class MyClass { public int Value { get; set; } public static MyClass operator =(MyClass a, int value) { a.Value = value; return a; } }
-
逻辑运算符 :
&&
,||
。cspublic class BooleanWrapper { private bool _value; public static BooleanWrapper operator &&(BooleanWrapper b1, BooleanWrapper b2) { return new BooleanWrapper { _value = b1._value && b2._value }; } public static BooleanWrapper operator ||(BooleanWrapper b1, BooleanWrapper b2) { return new BooleanWrapper { _value = b1._value || b2._value }; } }
重载运算符时,需要遵循一些规则和最佳实践:
-
运算符重载应该是直观的,并且与内置类型的行为一致。
-
重载的运算符应该保持对称性,例如,如果重载了
==
,也应该重载!=
。 -
重载
==
和!=
时,也应该重载GetHashCode
和Equals
方法。 -
重载
+
时,也应该考虑重载-
以及可能的+=
和-=
。 -
避免重载
&
,|
,&=
,|=
等位运算符,除非你的类型是位字段。