.NET入门:使调用更简单,扩展方法和泛型方法
Part 0 使用扩展方法,让实例也可以调用通用方法⚓
简单介绍一下扩展方法有什么用。我们还是举个例子💦,
我们还是有请🤖GPT为我们生成一段函数
public class StringAnalyzer
{
public static int CountA(string str)
{
if (string.IsNullOrEmpty(str))
return 0;
int count = 0;
foreach (char c in str)
{
if (char.ToUpper(c) == 'A')
count++;
}
return count;
}
}
这个⚙函数的作用是返回一个字符串中包含几个‘A’。
如果你需要使用这个函数应该给一个参数。
var number= StringAnalyzer.CountA("AAAAA2345");
接下来我们来使用扩展方法,这次我们写一个统计字符串里有几个B。
public static int CountB( this string str)
{
if (string.IsNullOrEmpty(str))
return 0;
int count = 0;
foreach (char c in str)
{
if (char.ToUpper(c) == 'B')
count++;
}
return count;
}
只需要给参数加个this就行了, 🌸C#规定扩展方法只能写在静态类里 🌸,所以你也需要给类添加静态修饰。
public static class StringAnalyzer
那么我们可以试试如何使用这个扩展方法。
var number2 = "BBBABBAAA".CountB();
对,只需要对符合this修饰的参数类型点一下就行了。只要使用扩展方法的类可以访问到定义类,就可以使用扩展方法。也就是说扩展方法public,并且包含的类被引用了,那么就可以被使用。
🌮 🌮 🌮
扩展方法和普通方法的定义没有什么区别,可以被重载,同样需要注意函数名的命名规范。挂载的this参数有且只能有一个,并且必须作为函数的第一个参数。其他参数没有限制。
🍥 🍥 🍥
虽然被标记了this,但和一般的函数一样,参数以值传递。也就是说你似乎对this参数修改值,如果this参数是一个值类型,那么修改是不起作用的。
💥你不能对this参数声明ref或者out! 💥
至此,扩展函数的部分讲完了,使用扩展函数能简化调用。
至于是否要使用,这点就仁者见仁智者见智了。
Part 1 什么是泛型✨
泛型的定义很简单,就是让你的代码与类型无关,这就是泛型,说起来很简单,但使用分很多方面。泛型集合,泛型类、泛型接口、泛型方法……
任何你使用了类型的地方都可以使用泛型。
我们先来看你最常使用的泛型集合。
我觉得这一部分不需要我特别的去讲,因为你一直都在使用。我们来举个例子💦
List<int> A= new List<int>();
Dictionary<int,string> B= new Dictionary<int,string>();
被尖括号包起来的就是泛型,字典和列表,以及其他集合类。都可以存储所有类型的数据,与数据类型无关,这就是泛型。🧱
Part 2 泛型方法,你还在为类型不确定而苦恼吗?
前文已经讲了,所谓泛型,就是你的代码不受到类型的约束就叫泛型。对于一个函数而言,我们往往希望他的参数或者某一部分,类型可以是多种,而不是特定的某种类型。
我们还是有请🤖GPT生成一些定义数据:
public class Animal
{
public string Breath { get; set; }
}
public class Monkey : Animal
{
public string Climb { get; set; }
}
public class Rabbit : Animal
{
public string Jump { get; set; }
}
public class Cat : Animal
{
public string Scratch { get; set; }
}
我们再编写一个函数用来构建对象🏐,并且输出对应信息,如果你不使用泛型:
public Animal ProcessAnimal(Animal animal)
{
Animal newAnimal = new Animal { Breath = "呼吸" };
switch (animal)
{
case Monkey monkey:
var monkeyNew = newAnimal as Monkey;
if (monkeyNew != null)
{
monkeyNew.Climb = "爬";
newAnimal = monkeyNew;
}
break;
case Rabbit rabbit:
var rabbitNew = newAnimal as Rabbit;
if (rabbitNew != null)
{
rabbitNew.Jump = "跳";
newAnimal = rabbitNew;
}
break;
case Cat cat:
var catNew = newAnimal as Cat;
if (catNew != null)
{
catNew.Scratch = "抓";
newAnimal = catNew;
}
break;
}
return newAnimal;
}
这里使用了switch判断类型,而不是判断aanimal🐒的值是什么。对于这个.NET引入的新功能,应该能看懂,这里不做过多的解释。
这里是需要你传一个参数,通过这个参数来创建一个对应类型的值并返回。
很显然这里我们需要的参数的类型,而不是参数的值,参数的值不是我们需要的。
public Animal ProcessAnimal<T>() where T : Animal, new()
{
Animal newAnimal = new T { Breath = "呼吸" };
switch (newAnimal)
{
case Monkey monkey:
monkey.Climb = "爬";
break;
case Rabbit rabbit:
rabbit.Jump = "跳";
break;
case Cat cat:
cat.Scratch = "抓";
break;
default:
// 默认选项,如果新动物不是已知的子类
return null;
}
return newAnimal;
}
于是我们有了一个新的函数🏄♀️,在这个函数中,我们传入了一个类型,也就是见尖括号里写的。这里的T就是一个需要在调用时给一个类型的“参数”,你把函数中的T命名成什么都不重要,你可以理解为泛型就是传递给方法的一个类型“参数”。调用如下:
Cat cat=ProcessAnimal<Cat>()
泛型不止可以只传类型,也可传对应类型的参数和返回值。
public T ProcessAnimal<T>() where T : Animal, new()
这和之前是一样的,因为你返回的对象其实是赋值了子类属性的子类对象,而不是父类。也就是说返回值的类型只和你传入的类型有关。
你可能会说,那我给T传一个不是Animal的类型,你这函数不就出问题了吗?那是不行的,因为T被加上了约束🔏。看见了括号后面的内容了吗?他的意思是接受T是一个Animal,并且Animal可以被new对象,接受0个参数的构造函数。
常见的约束🔏如下:
1.类类型约束 (class): 这要求泛型类型参数必须是引用类型。
csharp
public void MyMethod<T>(T item) where T : class2.结构体类型约束 (struct): 这要求泛型类型参数必须是值类型。
csharp
public void MyMethod<T>(T item) where T : struct3.新约束 (new()): 这要求泛型类型参数必须有一个无参数的>构造函数,允许你在方法内部创建该类型的新实例。
csharp
public T CreateInstance<T>() where T : new()
4.基类或接口约束: 你可以指定泛型类型参数必须是特定类或接口的子类或实现。
csharp
public void MyMethod<T>(T item) where T : MyBaseClass
public void MyMethod<T>(T item) where T : IMyInterface
当然了,这些约束条件可以被组合,如同之前动物🐓的例子一样,不同的约束条件之间用逗号隔开。
既然你可以在函数上使用泛型,自然也可以在委托上使用泛型。
使用方法都是一致的。
Part 2 泛型类、泛型接口,你的类可以处理更多类型的数据。
如果你理解了泛型方法,那么便可以举一反三理解泛型类、泛型接口。
泛型接口也就是,在你继承这个接口的类必须依照对应的约束条件🔑给一个类型“参数”。使用泛型语法和泛型方法一摸一样。
泛型函数也就是,在你new一个此类的对象时、或者继承这个类时,必须依照对应的约束条件🔗给一个类型“参数”。使用泛型语法和泛型方法一摸一样。
总结
泛型的使用就是给你的函数、类、接口提供了一个类型“参数”。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。