个人网站链接怎么做,宁波网站建设设计服务公司,济南网站建设cnwenhui,爱网站长尾关键词挖掘工具在《带你了解C#每个版本新特性》 一文中介绍了#xff0c;C# 1.0 到 7.0 的不同特性#xff0c;本文接着介绍在 8.0 和 9.0 中的一些常用新特性。C# 8.0在 dotNET Core 3.1 及以上版本中就可以使用 C# 8 的语法#xff0c;下面是 C# 8 中我认为比较常用的一些新功能。默认接… 在《带你了解C#每个版本新特性》 一文中介绍了C# 1.0 到 7.0 的不同特性本文接着介绍在 8.0 和 9.0 中的一些常用新特性。C# 8.0在 dotNET Core 3.1 及以上版本中就可以使用 C# 8 的语法下面是 C# 8 中我认为比较常用的一些新功能。默认接口方法接口是用来约束行为的在 C# 8 以前接口中只能进行方法的定义下面的代码在 C# 8 以前是会报编译错误的public interface IUser
{string GetName() oec2003;
}
那么在 C# 8 中可以正常使用上面的代码也就是说可以对接口中的方法提供默认实现。接口默认方法最大的好处是当在接口中进行方法扩展时之前的实现类可以不受影响而在 C# 8 之前接口中如果要添加方法所有的实现类需要进行新增接口方法的实现否则编译失败。C# 中不支持多重继承主要的原因是会导致菱形问题类 A 是一个抽象类定义有一个 方法 Test类 B 和 类 C 继承自抽象类 A并有各自的实现类 D 同时继承类 B 和类 C当调用类 D 的 Test 方法时就不知道应该使用 B 的 Test 还是 C 的 Test这个就是菱形问题。而接口是允许多继承的那么当接口支持默认方法时是否也会导致菱形问题呢看下面代码public interface IA
{void Test() Console.WriteLine(Invoke IA.Test);
}
public interface IB:IA
{void Test() Console.WriteLine(Invoke IB.Test);
}
public interface IC:IA
{ void Test() Console.WriteLine(Invoke IC.Test);
}
public class D : IB, IC { }static void Main(string[] args)
{D d new D();d.Test();
}
上面的代码是无法通过编译的因为接口的默认方法不能被继承所以类 D 中没有 Test 方法可以调用如下图所以必须通过接口类型来进行相关方法的调用static void Main(string[] args)
{IA d1 new D();IB d2 new D();IC d3 new D();d1.Test(); // Invoke IA.Testd2.Test(); // Invoke IB.Testd3.Test(); // Invoke IC.Test
}
也正是因为必须通过接口类型来进行调用所以也就不存在菱形问题。而当具体的类中有对接口方法实现的时候就会调用类上实现的方法public interface IA
{void Test() Console.WriteLine(Invoke IA.Test);
}
public interface IB:IA
{void Test() Console.WriteLine(Invoke IB.Test);
}
public interface IC:IA
{ void Test() Console.WriteLine(Invoke IC.Test);
}
public class D : IB, IC
{public void Test() Console.WriteLine(Invoke D.Test);
}
static void Main(string[] args)
{IA d1 new D();IB d2 new D();IC d3 new D();d1.Test(); // Invoke D.Testd2.Test(); // Invoke D.Testd3.Test(); // Invoke D.Test
}
类可能同时继承类和接口这时会优先调用类中的方法public class A
{public void Test() Console.WriteLine(Invoke A.Test);
}
public interface IA
{void Test() Console.WriteLine(Invoke IA.Test);
}public class D : A, IA { }
static void Main(string[] args)
{D d new D();IA d1 new D();d.Test(); // Invoke A.Testd1.Test(); // Invoke A.Test
}
关于默认接口方法总结如下默认接口方法可以让我们在往底层接口中扩展方法的时候变得比较平滑默认方法会优先调用类中的实现如果类中没有实现才会去调用接口中的默认方法默认方法不能够被继承当类中没有自己实现的时候是不能从类上直接调用的。using 变量声明我们都知道 using 关键字可以导入命名空间也能定义别名还能定义一个范围在范围结束时销毁对象在 C# 8.0 中的 using 变量声明可以让代码看起来更优雅。在没有 using 变量声明的时候我们是这样使用的static void Main(string[] args)
{var connString Host221.234.36.41;Usernamegpadmin;Password123456;Databasepostgres;Port54320;using (var conn new NpgsqlConnection(connString)){conn.Open();using (var cmd new NpgsqlCommand(select * from user_test, conn)){using (var reader cmd.ExecuteReader()){while (reader.Read())Console.WriteLine(reader[user_name]);}}}Console.ReadKey();
}
当调用层级比较多时会出现 using 的嵌套对影响代码的可读性当然当两个 using 语句中间没有其他代码时可以这样来优化static void Main(string[] args)
{var connString Host221.234.36.41;Usernamegpadmin;Password123456;Databasepostgres;Port54320;using (var conn new NpgsqlConnection(connString)){conn.Open();using (var cmd new NpgsqlCommand(select * from user_test, conn))using (var reader cmd.ExecuteReader())while (reader.Read())Console.WriteLine(reader[user_name]);}Console.ReadKey();
}
使用 using 变量声明后的代码如下static void Main(string[] args)
{var connString Host221.234.36.41;Usernamegpadmin;Password123456;Databasepostgres;Port54320;using var conn new NpgsqlConnection(connString);conn.Open();using var cmd new NpgsqlCommand(select * from user_test, conn);using var reader cmd.ExecuteReader();while (reader.Read())Console.WriteLine(reader[user_name]);Console.ReadKey();
}
Null 合并赋值这是一个很有用的语法糖在 C# 中如果调用一个为 Null 的引用类型上的方法会出现经典的错误”未将对应引用到对象的实例“所以我们在返回引用类型时需要做些判断static void Main(string[] args)
{Liststring list GetUserNames();if(listnull){list new Liststring();}Console.WriteLine(list.Count);
}
public static Liststring GetUserNames()
{return null;
}
在 C# 8 中可以使用 ?? 操作符更简单地实现static void Main(string[] args)
{Liststring list GetUserNames();list ?? new Liststring();Console.WriteLine(list.Count);
}
当 list 为 null 时会将右边的值分配给 list 。C# 9.0在 .NET 5 中可以使用 C# 9 下面是 C# 9 中几个常用的新特性。initinit 是属性的一种修饰符可以设置属性为只读但在初始化的时候却可以指定值:public class UserInfo
{public string Name { get; init; }
}
UserInfo user new UserInfo { Name oec2003 };
//当 user 初始化完了之后就不能再改变 Name 的值
user.Name oec2004;
上面代码中给 Name 属性赋值会出现编译错误record在 C# 9 中新增了 record 修饰符record 是一种引用类型的修饰符使用 record 修饰的类型是一种特别的 class一种不可变的引用类型。我们创建一个名为 UserInfo 的 class 不同的实例中即便属性值完全相同这两个实例也是不相等的看下面代码public class UserInfo
{public string Name { get; set; }
}
static void Main(string[] args)
{UserInfo user1 new UserInfo { Name oec2003 };UserInfo user2 new UserInfo { Name oec2003 };Console.WriteLine(user1 user2); //False
}
如果使用 record 将会看到不一样的结果因为 record 中重写了 、Equals 等 是按照属性值的方式来进行比较的public record UserInfo
{public string Name { get; set; }
}
static void Main(string[] args)
{UserInfo user1 new UserInfo { Name oec2003 };UserInfo user2 new UserInfo { Name oec2003 };Console.WriteLine(user1 user2); //True
}
在 class 中我们经常将一个对象的实例赋值给另一个值对赋值后的对象实例进行属性值的改变会影响到原对象实例public class UserInfo
{public string Name { get; set; }
}
static void Main(string[] args)
{UserInfo user new UserInfo { Name oec2003 };UserInfo user1 user;user1.Name oec2004;Console.WriteLine(user.Name); // oec2004
}
如果想要不影响原对象实例就需要使用到深拷贝在 record 中可以使用 with 语法简单地达到目的public record UserInfo
{public string Name { get; set; }
}
static void Main(string[] args)
{UserInfo user new UserInfo { Name oec2003 };UserInfo user1 user with { Nameeoc2004};Console.WriteLine(user.Name); // oec2003Console.WriteLine(user1.Name); // oec2004
}
模式匹配增强模式匹配中我觉得最有用的就是对 Null 类型的判断在 9.0 中支持这样的写法了public static string GetUserName(UserInfo user)
{if(user is not null){return user.Name;}return string.Empty;
}
顶级语句这个不知道有啥用但挺好玩的创建一个控制台程序将 Program.cs 中的内容替换为下面这一行程序也能正常运行System.Console.WriteLine(Hello World!);
除此之外在 C# 8.0 和 9.0 中还有一些其他的新功能我目前没有用到或者我觉得不太常用就没有写在本文中了。希望本文对您有所帮助。