.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 : class

2.结构体类型约束 (struct): 这要求泛型类型参数必须是值类型。

csharp
public void MyMethod<T>(T item) where T : struct

3.新约束 (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一个此类的对象时、或者继承这个类时,必须依照对应的约束条件🔗给一个类型“参数”。使用泛型语法和泛型方法一摸一样。

总结

泛型的使用就是给你的函数、类、接口提供了一个类型“参数”。


wang_hun
9 声望1 粉丝