C# 进阶语法

x33g5p2x  于2022-05-11 转载在 其他  
字(5.9k)|赞(0)|评价(0)|浏览(376)

C# 进阶语法

快捷键

  • main方法:svm + tab
  • 构造方法:ctor + tab

其他关键词/修饰符

IntPtr

一种特定于平台的类型,用于表示指针或句柄。

SafeHandler

表示操作系统句柄的包装类。这个类必须被继承。

readonly

  • 在字段声明中, 指示只能在声明期间或在同一个类的构造函数中向字段赋值。 可以在字段声明和构造函数中多次分配和重新分配只读字段。
  • 在readonly struct 类型定义中,readonly 指示结构类型是不可变的。
  • 在结构类型内的实例成员声明中,readonly 指示实例成员不修改结构的状态。
  • 在方法返回中,readonly 修饰符指示该方法返回一个引用,且不允许向该引用写入内容。

C#各种方法

静态方法

生命周期:一旦创建直到应用程序结束才会消失,全局。
方法被static修饰,可以直接通过类名.方法名调用

internal class TestMethodClass
    {
        static void Main(string[] args)
        {
            string a  = TestMethodClass.StaticMethod();
            Console.Read();
        }

        public static string StaticMethod() { 
             return "This is Static Method";
        }
    }

构造方法

类似于java,默认是有一个无参构造方法,但是不会显示出来。可以自定义多个有参构造方法。在实例化类的时候(也就是生命周期开始时)会去调用构造方法在内存中开辟一段空间,存储这个实例化对象。
方法名与类名一致。

折构方法

声明折构方法:~类名
一般在GC回收时调用,用来释放对象。一般很少用到。
这里涉及到托管资源的概念,简单来讲GC机制会自动回收一些资源,但是有一些资源不在GC回收机制范围内,这部分就是非托管资源,而折构方法一般用在回收非托管资源上面,比如数据库链接。

C#中IDisposable接口的主要用途是释放非托管资源。当不再使用托管对象时,垃圾回收器会自动释放分配给该对象的内存。但无法预测进行垃圾回收的时间。另外,垃圾回收器对窗口句柄或打开的文件和流等非托管资源一无所知。将此接口的Dispose方法与垃圾回收器一起使用来显式释放非托管资源。当不再需要对象时,对象的使用者可以调用此方法。Dispose()专门回收GC机制回收不了的非托管资源
使用时大概如下:
在一个类中实现IDisposable,重写Dispose方法,方法内为回收非托管资源的逻辑,之后声明折构方法:~类名,折构方法在对象销毁时触发,而在折构方法内部调用重写的Dispose()方法实现非托管资源的回收。

internal class DisposeTest : IDisposable
{
    public void Dispose()
    {
        throw new NotImplementedException();
    }
}

虚/重写方法

  • 虚方法:virtual关键词修饰
  • 重写方法:override关键词修饰
    可被子类重写该方法,重写时里面可以通过base.虚方法名来调用原方法内的逻辑。也可以修改虚方法内容改为新的逻辑,即便修改后也不会更改原虚方法内的方法逻辑。
    感觉类似于一个可以实现方法逻辑的抽象方法,可以被其他子类重写,但自定义的虚方法内部可以自定义功能。
public class VirtualMethod
{
    public virtual int Calc(int a, int b)
    {
        return a + b;
    
    }    
    
}

public class VirtualMethodChild : VirtualMethod {
    public override int Calc(int a, int b)
    {
        return base.Calc(a, b); 
    }
}

抽象方法

abstract关键词修饰,抽象方法必须写到抽象类里面。一般由抽象类定义抽象方法的规范(入参/返回值)之后子类实现抽象方法,完成方法内部具体逻辑。

扩展方法

使用场景,当一个类设置了sealed修饰符,那这个类就是密封的,不可以被继承,也就不可以通过继承的方式重写这个类中的方法然后去调用了。

  1. 调用密封类的对象、属性、方法等(扩展密封类)
  2. 扩展接口

0x01 扩展方法
比如这里拿sealed修饰了一个类,但是想调用这个里面的getString方法

internal sealed class Class4
{
    public string getString() {
        return "string";
    }
}

定义扩展方法:静态类+静态方法+this 引用类型
调用时直接对象名.方法名即可调用

static class Program
{

    public static void testExtendMethod(this ExtendClass extclass) {
        extclass.getString();
    }
}

0x02 扩展接口
接口

internal interface Interface1
    {
        int Add(int a, int b);
    }

定义扩展方法,同时新增了一个方法,这样当有一个新的类继承原来的接口Interface1时也可以调用到新增的扩展方法ExtendAdd2

public static  class ExtendClassTest
{
    public static int ExtendAdd1(this Interface1 in1, int a, int b) { 
        return a + b;
    }

    public static int ExtendAdd2(this Interface1 in1, int a, int b)
    {
        return a - b;
    }
}

泛型

允许延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。
比如设置数组,用object赋值的时候 元素可以为int 、string等其他类型。和java泛型差不多。再比如下面的使用泛型类,在类名后跟<T>,占位符不一定是T其他的也可以,但是默认是写成T。可以等到真正使用的时候再去设置这个类声明的一个类型。再有这里泛型不一定只是定义一个,也可以定义多个。

public class MyGenericArray<T>
{
    private T[] array;
    public MyGenericArray(int size)
    {
        array = new T[size + 1];
    }
    public T getItem(int index)
    {
        return array[index];
    }
    public void setItem(int index, T value)
    {
        array[index] = value;
    }
}
       
class Tester
{
    static void Main(string[] args)
    {
        // 声明一个整型数组
        MyGenericArray<int> intArray = new MyGenericArray<int>(5);
        // 设置值
        for (int c = 0; c < 5; c++)
        {
            intArray.setItem(c, c*5);
        }
        // 获取值
        for (int c = 0; c < 5; c++)
        {
            Console.Write(intArray.getItem(c) + " ");
        }
        Console.WriteLine();
        // 声明一个字符数组
        MyGenericArray<char> charArray = new MyGenericArray<char>(5);
        // 设置值
        for (int c = 0; c < 5; c++)
        {
            charArray.setItem(c, (char)(c+97));
        }
        // 获取值
        for (int c = 0; c < 5; c++)
        {
            Console.Write(charArray.getItem(c) + " ");
        }
        Console.WriteLine();
        Console.ReadKey();
    }
}

泛型方法

泛型方法,在方法名后跟<T>

class Program
    {
        static void Swap<T>(ref T lhs, ref T rhs)
        {
            T temp;
            temp = lhs;
            lhs = rhs;
            rhs = temp;
        }
        static void Main(string[] args)
        {
            int a, b;
            char c, d;
            a = 10;
            b = 20;
            c = 'I';
            d = 'V';

            // 在交换之前显示值
            Console.WriteLine("Int values before calling swap:");
            Console.WriteLine("a = {0}, b = {1}", a, b);
            Console.WriteLine("Char values before calling swap:");
            Console.WriteLine("c = {0}, d = {1}", c, d);

            // 调用 swap
            Swap<int>(ref a, ref b);
            Swap<char>(ref c, ref d);

            // 在交换之后显示值
            Console.WriteLine("Int values after calling swap:");
            Console.WriteLine("a = {0}, b = {1}", a, b);
            Console.WriteLine("Char values after calling swap:");
            Console.WriteLine("c = {0}, d = {1}", c, d);
            Console.ReadKey();
        }
    }

泛型约束

这里直接贴图,简单了解,后面遇到了再深入跟一下。
泛型约束,通过在参数声明之后,通过where关键词指定传入的泛型的类型约束,比如这里where T:new() 就表示传来的泛型T必须是一个类,可以通过new去实例化。

常用的约束如下,接口约束可以有多个

协变和逆变

out/in 修饰符

out修饰的参数只能当作返回值用,不可以被用做内部的具体方法的入参。

in修饰的只能当作参数,不能当作返回值。

委托

C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。
委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。

委托声明

声明格式如下

delegate <return type> <delegate-name> <parameter list>

上面的委托可被用于引用任何一个带有一个单一的 string 参数的方法,并返回一个 int 类型变量。
这里感觉就是在内部回去匹配一个入参数量为1,类型为string,且返回值类型为int的方法,并用MyDelegate作为该方法的引用。

public delegate int MyDelegate (string s);

委托实例化

个人感觉类似于一个代理机制,实例化DelegateTest时传入相应的(返回值类型,入参数量,入参类型)方法,即可通过DelegateTest的实例化对象调用该方法(感觉是创建了一个对于该方法的引用)

delegate string DelegateTest (string str);
namespace Reflection
{
    public class MyReflection
    {
        public static string Base = "base";
        public static string Hello(string str) { 
            Base += "Hello " + str;
            return Base;
        }
        public static string World(string str) {
            Base +=  "World " + str;
            return Base;
        }

        static void Main(string[] args)
        {
            DelegateTest delegateTest1 = new DelegateTest(Hello);
            DelegateTest delegateTest2 = new DelegateTest(World);
            Console.WriteLine(Base);
            delegateTest1("delegateTest1");
            Console.WriteLine(Base);
            delegateTest2("delegateTest2");
            Console.WriteLine(Base);

            Console.ReadKey();   
        }
        
output:
base
baseHello delegateTest1
baseHello delegateTest1World delegateTest2

委托多播

委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。
执行的话应该是也有顺序的,按照+前后的顺序来执行。

delegate string DelegateTest (string str);
namespace Reflection
{
    public class MyReflection
    {
        public static string Base = "base";
        public static string Hello(string str) { 
            Base += "Hello " + str;
            return Base;
        }
        public static string World(string str) {
            Base +=  "World " + str;
            return Base;
        }

        static void Main(string[] args)
        {
            DelegateTest delegateTest1 = new DelegateTest(Hello);
            DelegateTest delegateTest2 = new DelegateTest(World);
            DelegateTest dt = delegateTest1 + delegateTest2;
            dt("delegateTest");
            Console.WriteLine(Base);

output:
baseHello delegateTestWorld delegateTest

所有内容仅限于维护网络安全学习参考

相关文章