【C#】从Java到C#

前言

1

一、基本数据类型

1

精度 : float < double < decimal

动态类型(运行时确定类型) : dynamic

C# 是静态语言(编译时检查数据类型),可以使用dynamic

dynamic obj = new Game();
obj = "str";
Console.WriteLine(obj.GetType());

二、sealed 类与成员

防止类继承、防止派生类重写,可以修饰类或类成员

三、构造

  • 先调用基类构造( if exist base.fuMethod ) --> 才是 子类构造
  • :base(参数?)继承父类构造,先调用父类构造

四、向上转型和向下转型

  • 向上 : 把一个派生类类型 转换为他的基类
  • 向下 : 把一个基类转换为某个派生类

转换使用as关键字 可以防止 抛出异常

Car c = (Car)obj;    // 可能会抛出异常,终止程序运行

Car c = obj as Car;  // 不会抛出异常
if(c != null){
	// 执行后续操作
}

is关键字 :检查对象类型

if(obj is Car){
	Car c = (Car)obj; // 绝对不会出错
}

五、装箱和拆箱

装箱

  • 一个值类型转换为引用类型的过程
int i = 10;
object o = i;
// or
object o2 = 10; 
  • 基本类型变为对象类型
  • 值类型变为引用类型
  • 保存位置从stack变为heap
  • 装箱就是把基本数据使用对象包装起来,一个装箱子的过程

拆箱

  • 从对象中打开,提取基本数据类型
object obj = 10;
int num = (int)obj;

使用装箱和拆箱导致

  1. 类型不安全
  2. 性能影响

六、虚方法和方法重写

  • 虚方法 virtual
    • 父类定义方法
  • 方法重写 override
    • 修改基类方法的执行逻辑,定义子类独有的方法

七、多态

  • 父类引用指向子类对象
  • 编译看左边,运行看右边
  • 方法参数使用接口or父类,传入参数为 接口的实现类or子类 ( 可插拔式,不动源码,只修改实现类or子类
    public void ActionHandler(List<Person> persons)
    {
        foreach (var p in persons)
        {
            p.SayHello();
        }
    }

防御性编程
传入类or接口参数 --> 方法内部接收后 先判断方法bool 是否存在or是否已完成 等…
1
接口与多态

八、抽象类与抽象方法

abstract

  • 声明修饰符,修饰类、方法、属性
  • 只有声明,没有方法内容

声明为abstract的属性或方法不能有代码实现
某个成员是abstractr修饰,那么class也要使用abstract修饰
派生类必须实现抽象类中声明的所有抽象方法与属性
抽象类不可实例化

九、控制反转与依赖注入

  • 将控制创建(new)对象的权力反转交给 容器
  • 通过DI 将容器中创建的对象 取出 使用
// 配置IOC
ServiceCollection serviceCollection = new ServiceCollection();

// singleton , 单例模式 ( 整个项目就一个
// scoped , 作用域模式   ( 每一次请求 都是一个作用域 ,请求结束 就结束
// transient , 瞬时模式 ( 一次性
serviceCollection.AddScoped<IService, Service>();

// 从IOC提取服务
ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
IService? service = serviceProvider.GetService<IService>();

// 执行方法
service.DoSomething();

十、Struct

简单版的Class

  • class 对象是引用类型,保存在head内存,执行效率低
  • struct 值类型,保存stack内存中,运行效率高

一些面向对象特性不能使用,比如继承,不能定义无参构造

十一、泛型

  • T 定义在方法上
public class Utility
{ 	// 判断 IComparable 比较接口
    public T FindMax<T>(T a, T b) where T: IComparable
    {
        return a.CompareTo(b) > 0 ? a : b;
    }
}
  • T 定义在类上
public class Utility<T> where T : IComparable
{
    public T FindMax(T a, T b)
    {
        return a.CompareTo(b) > 0 ? a : b;
    }
}

where T : IComparable
where T : class(product,book)
where T : struct
where T : new()

十二、为空处理

int? i = null; // ?可为空 
int normalInt = nullNum ?? 0; // 如果nullNum为空,则被赋值为0 

值对象不可为空null
声明值对象,C#编译器会默认赋值
bool => false
整数 => 0
浮点数 => 0
DateTime 值类型,需要特殊处理才能为null , 使用 ??
date ?? DateTime.Today;

十三、拓展方法this

  • 不需要继承原Class
  • 直接在方法上参数标注 this XX xx

参数 : this string str这个 string 是拓展的方法

public static class StringExtension
{
    public static string ToUpperCase(this string str)
    {
        return str.ToUpper();
    }

    public static string ToShortTerm(this string str, int number)
    {
        return str.Substring(0, number);
    }
}

十四、反射 与 元数据

  • 反射
    要实例化对象,但是使用new,不知道目标类型
    在运行的过程查看或使用元数据的过程就叫反射
  • 元数据
    关于数据的数据 (数据库、文件上的数据,程序本身也是数据)

元数据报存在程序集(Assembly)中:
在这里插入图片描述
示例 :
在这里插入图片描述

string classLocation = "HelloCSharp.Advanced.MyList"; // 命名空间.类名
Type? objType = Type.GetType(classLocation); // HelloCSharp.Advanced.MyLists
MethodInfo? add = objType.GetMethod("Add"); // Void Add()
object? instance = Activator.CreateInstance(objType); // 创建实例
add.Invoke(instance, null); // 传入对象,对象参数

程序的说明文档:

  • 程序有多少类
  • 分别保存在什么位置
  • 每个类有什么可执行的方法
  • 有什么可访问的字段和属性

十五、委托与事件

委托 :
Action<> 无返回值
Func<> 有返回值

事件 :

数据、操作、通知

事件

  • 内置事件
    定时器,一秒触发一次
private readonly Timer _sendTimer; // 声明定义

_sendTimer = new Timer(1000); // 触发条件
_sendTimer.Elapsed += SendTimer_Elapsed; // 订阅方法

// 方法实现
private async void SendTimer_Elapsed(object sender,ElapsedEventArgs e)
{
            await SendDataAsync();
}

当触发条件,即执行订阅方法

  • 自定义事件 (有参)
    当有数据传递,触发方法
public event EventHandler<(string DeviceId, float[] Data)> DataReceived; //事件定义

// 触发 DataReceived 事件,将数据传递给订阅者
 OnDataReceived(dataArr); // 触发条件

// 订阅方法
modbus.DataReceived += (sender, args) => OnDataReceived(args.DeviceId, args.Data);

// 执行订阅方法
private void OnDataReceived(float[] data)
{
   DataReceived?.Invoke(this, (_deviceId, data));
}

GPT无参事件示例:
在这里插入图片描述

触发事件时要检查是否有订阅者,以避免 NullReferenceException
事件可以携带数据,也可以是无参的

十六、集合Collection

// sort升序
Arrary.Sort(nums);
// Reverse 对数组进行反转
Array.Reverse(nums)

在这里插入图片描述

AddRange(添加数组/集合)  
    
InsertRange(插入位置,插入数组/集合)
    Insert(位置,插入元素)
    
RemoveRange(删除位置起始,结束位置)	 // 删除
	RemoveAt(位置)
    Remove(某元素)
    RemoveAll(Lambda) 	//符合条件的全部删除		
    			集合.RemoveAll(i=>i=="某元素");
				集合.RemoveAll(i=>i.Contains("某元素")); // 删除包含某元素的数据

// 读取数据
集合.Count; // 获取集合当前元素数量
集合.Capacity; // 获取集合当前容量

// 索引器
var num = 集合[1];

-------------------------------------------------------

// 定义集合
var ints = new List<string>()
{
    "道格",
    "维克",
    "格洛弗斯"
};

// 迭代器
List<string>.Enumerator enumerator = ints.GetEnumerator();
while (enumerator.MoveNext())
{
    Console.WriteLine(enumerator.Current);
}

// foreach 
// 基本类型只能遍历 内部操作集合会报错 ( 只读数据 
foreach (var item in ints)
{
    Console.WriteLine(item);
}

// 引用类型 可以内部操作 因为改的是地址
foreach (var p in Person)
{
    p.Name = "123";
    Console.WriteLine(p);
}

//普通for 可遍历,内部操作集合
for (var i = 0; i < ints.Count; i++)
{
    Console.WriteLine(ints[i]);
}

十七、 IEnumerable<T>yield return

迭代器与懒加载

示例一 : 使用 IEnumerable 和 yield return 实现自定义过滤器
将偶数一条一条过滤出来

public class NumberFilter
{
    public static IEnumerable<int> GetEvenNumbers(IEnumerable<int> numbers)
    {
        foreach (var number in numbers)
        {
            if (number % 2 == 0)
            {
                yield return number;
            }
        }
    }
}

class Program
{
    static void Main()
    {
        var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        foreach (var evenNumber in NumberFilter.GetEvenNumbers(numbers))
        {
            Console.WriteLine(evenNumber);
        }
    }
}

示例二 : 对象

public class Game : IEnumerable<Player>
{
    private List<Player> Players { get; set; } = new List<Player>();

    public Game()
    {
        Players.Add(new Player(1, "道格", "PC"));
        Players.Add(new Player(2, "维克", "Mac"));
        Players.Add(new Player(3, "格洛弗斯", "PS5"));
        Players.Add(new Player(4, "尤金", "Xbox"));
    }
	// 使用泛型迭代器输出
    public IEnumerator<Player> GetEnumerator()
    {
        return Players.GetEnumerator();
    }
}
// 实体类定义
public class Player
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string device { get; set; }

    public Player(int id, string name, string device)
    {
        Id = id;
        Name = name;
        this.device = device;
    }
}

通常用于需要按需生成、过滤或遍历大量数据的场景

yield return :
懒加载,除非数据要被使用,否则不会创建对象;它不是返回数据,而是数据的迭代。
简单地说,当希望获取一个IEnumerable<T>类型的集合,而不想把数据一次性加载到内存,就可以考虑使用yield return实现"按需供给"。

static IEnumerable<int> createEnumerable()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

foreach (var i in createEnumerable())
{
    Console.WriteLine(i);
}

十八 、字典 Dictionary

键值对

  • 基本使用
class Program
{
    static void Main()
    {
        // 创建一个 Dictionary,键为字符串,值为整数
        Dictionary<string, int> ages = new Dictionary<string, int>();

        // 添加键值对
        ages["Alice"] = 30;
        ages["Bob"] = 25;
        ages["Charlie"] = 35;

        // 访问值
        Console.WriteLine($"Alice's age: {ages["Alice"]}");

        // 检查键是否存在
        if (ages.ContainsKey("Bob"))
        {
            Console.WriteLine($"Bob's age: {ages["Bob"]}");
        }

        // 更新值
        ages["Charlie"] = 36;
        Console.WriteLine($"Charlie's updated age: {ages["Charlie"]}");

        // 遍历所有键值对
        foreach (var kvp in ages)
        {
            Console.WriteLine($"{kvp.Key} is {kvp.Value} years old.");
        }
    }
}
  • TryGetValue 方法,可以安全地尝试获取字典中的值,避免因键不存在而抛出异常
        // 创建一个 Dictionary,将国家代码映射到国家名称
        Dictionary<string, string> countryCodes = new Dictionary<string, string>
        {
            { "US", "United States" },
            { "CA", "Canada" },
            { "JP", "Japan" }
        };

        // 根据国家代码获取国家名称
        string countryCode = "CA";
        if (countryCodes.TryGetValue(countryCode, out string countryName))
        {
            Console.WriteLine($"{countryCode} stands for {countryName}");
        }
        else
        {
            Console.WriteLine($"Country code {countryCode} not found.");
        }

十九、LINQ

常用的 LINQ 方法 :
where
内部有 yield return 延迟加载,根据条件过滤集合中的元素

select
数据投影,集合中的每个元素投影到新的形式(即映射元素)

        List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
        var lengths = names.Select(name => name.Length);
        foreach (var length in lengths)
        {
            Console.WriteLine(length); // 输出:5, 3, 7
        }

select
Any()
是否存在某元素,返回bool

ALL()
是否所有元素都包括 这个关键字

OrderBy OrderByDescending:排序
作用:对集合中的元素按升序或降序排序。

// 降序排列Desc
var queryLambda = new DirectoryInfo(path).GetFiles().OrderByDescending(f => f.Length).Take(5);
// 顺序排列ASC
var queryLambda = new DirectoryInfo(path).GetFiles().OrderBy(f => f.Length).Take(5);

GroupBy:分组 将集合中的元素按指定的键分组

        List<string> names = new List<string> { "Alice", "Bob", "Charlie", "Anna", "Bryan" };

        var groupedNames = names.GroupBy(name => name[0]); //获取每个元素的第一个字符,作为键key

        foreach (var group in groupedNames)
        {
            Console.WriteLine($"Names starting with '{group.Key}':");

            foreach (var name in group)
            {
                Console.WriteLine(name); // 输出:Names starting with 'A': Alice, Anna
                                         //       Names starting with 'B': Bob, Bryan
                                         //       Names starting with 'C': Charlie
            }
        }

示例二 : 分组 + 内部排序 + 只取2条

var queryGroup = cars.GroupBy(c => c.Manufacturer).OrderByDescending(g => g.Key); // Manufacturer制造商作为键
foreach (var g in queryGroup)
{
    Console.WriteLine($"{g.Key}{g.Count()}.");
    foreach (var car in g.OrderByDescending(c => c.Combined).Take(2))
    {
        Console.WriteLine($"{car.Model}{car.Combined}");
    }
}

在这里插入图片描述
Join:联接
在两个集合之间执行内连接,类似于 SQL 中的 JOIN 操作。’

  class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    class Course
    {
        public int StudentId { get; set; }
        public string CourseName { get; set; }
    }

    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Id = 1, Name = "Alice" },
            new Student { Id = 2, Name = "Bob" },
            new Student { Id = 3, Name = "Charlie" }
        };

        List<Course> courses = new List<Course>
        {
            new Course { StudentId = 1, CourseName = "Math" },
            new Course { StudentId = 1, CourseName = "Physics" },
            new Course { StudentId = 2, CourseName = "Chemistry" }
        };

        var studentCourses = students.Join(courses,
            student => student.Id,
            course => course.StudentId,
            (student, course) => new
            {
                StudentName = student.Name,
                CourseName = course.CourseName
            });

        foreach (var sc in studentCourses)
        {
            Console.WriteLine($"{sc.StudentName} is enrolled in {sc.CourseName}"); 
            // 输出:Alice is enrolled in Math
            //      Alice is enrolled in Physics
            //      Bob is enrolled in Chemistry
        }
    }

group join 分组的过程中加入第二个数据源

var qery = manu.GroupJoin(cars, m => m.Name, c => c.Manufacturer,
        (m, c) => new { Manufacturer = m, Cars = c })
    .OrderByDescending(m => m.Manufacturer.Name);


foreach (var g in qery)
{
    Console.WriteLine($"{g.Manufacturer.Name}{g.Cars.Count()}辆,{g.Manufacturer.Phone}");
    foreach (var car in g.Cars.OrderByDescending(c=>c.Combined).Take(2))
    {
        Console.WriteLine($"\t {car.Model} : {car.Combined}");
    }
}
![分组](https://i-blog.csdnimg.cn/direct/12adec35266545eb942c18633bbf5ce7.png)

分组聚合

var query = cars
    .GroupBy(car => car.Manufacturer)
    .Select(carGroup => new
    {
        Manufacturer = carGroup.Key,
        Avg = carGroup.Average(c => c.Combined),
        Max = carGroup.Max(c => c.Combined),
        Min = carGroup.Min(c => c.Combined)
    });

foreach (var group in query)
{
    Console.WriteLine($"{group.Manufacturer} 油耗情况");
    Console.WriteLine($"  最高: {group.Max}");
    Console.WriteLine($"  最低: {group.Min}");
    Console.WriteLine($"  平均: {group.Avg}");
}

FirstFirstOrDefault:获取第一个元素
作用:返回集合中的第一个元素。
First 方法在集合为空时抛出异常,
而 FirstOrDefault 在集合为空时返回默认值(如 null)

二十、异步编程

  • async 函数本身并不创建异步操作,只有调用await的时候才会执行异步操作。
  • 上下文关键字,想要异步调用一个async函数,自己必须也是async函数。
  • 返回类型:void,TaskTask<T>,IAsyncEnumerable<T>

二十二、Dispose

Dispose 可以用来回收比如数据库连接,文件读取,HTTP长连接等无法托管在.NET平台中的外部资源

使用Dispose 必须实现 IDisposable 接口

IDisposable 接口配置using关键字才能完成生命周期的托管

二十三、参数、关键字

out参数与ref参数

out : 返回方法执行完后的数据

    static void Main()
    {
        int result;
        Calculate(10, 20, out result);
        Console.WriteLine($"Result is {result}"); // 输出:Result is 30
    }

    static void Calculate(int a, int b, out int sum)
    {
        sum = a + b; // 必须在方法内部赋值
    }

ref : 形参在方法中发生的改变,会影响到实参。(形参与实参 连接 ,方法内改变形参,实参也发生变化)

    static void Main()
    {
        int number = 10;
        DoubleValue(ref number);
        Console.WriteLine($"Doubled value is {number}"); // 输出:Doubled value is 20
    }

    static void DoubleValue(ref int value)
    {
        value *= 2; // 修改传入的参数值
    }

params可变参数

**作用:**允许向方法传递不定数量的参数。它使得方法可以接收任意数量的相同类型的参数,或者不传递任何参数。
**场景:**当你不确定传递给方法的参数数量时使用。

    static void Main()
    {
        PrintNumbers(1, 2, 3, 4, 5); // 传递多个参数
        PrintNumbers(); // 不传递参数
    }

    static void PrintNumbers(params int[] numbers)
    {
        foreach (var number in numbers)
        {
            Console.WriteLine(number); // 输出参数
        }
    }

全局静态变量

作用: 静态变量属于类,而不是实例。全局静态变量可以在类的所有实例间共享数据。它在类的整个生命周期中都存在。
场景: 当需要在类的不同实例间共享数据,或创建与类关联的常量时使用。

class Program
{
    static void Main()
    {
        Counter.Increment();
        Counter.Increment();
        Console.WriteLine($"Counter: {Counter.Count}"); // 输出:Counter: 2
    }
}

static class Counter
{
    public static int Count = 0; // 全局静态变量

    public static void Increment()
    {
        Count++;
    }
}

new 关键字

  1. 创建对象
  2. 隐藏基类成员 : 当派生类中的成员与基类中的成员同名时,可以使用 new 关键字隐藏基类成员
    (隐藏的后果是子类调用不到父类成员,父子同名-> 子类方法上使用)

场景:
在派生类中需要隐藏基类成员时使用

class Program
{
    static void Main()
    {
        DerivedClass derived = new DerivedClass();
        derived.Show(); // 输出:This is DerivedClass
    }
}

class BaseClass
{
    public void Show()
    {
        Console.WriteLine("This is BaseClass");
    }
}

class DerivedClass : BaseClass
{
    public new void Show() // 隐藏基类的 Show 方法
    {
        Console.WriteLine("This is DerivedClass");
    }
}

总结

out 和 ref:用于方法参数传递,out 用于返回多个值,ref 用于修改原始变量的值。
params:用于接收不定数量的参数。
全局静态变量:在类的所有实例之间共享数据。
new:用于创建对象实例,或在派生类中隐藏基类成员。

拓展

.sln 与 .csproj

.sln : 解决方案,双击运行

.csproj : 项目文件,包含项目信息,双击运行

字符串

string.join(",",集合)  // 集合元素连接方式“,”

反射与文件获取 实例

//将当前工程文件夹与USB地址连接
string USBInterface = Path.Combine(Environment.CurrentDirectory, "USB"); 
// 地址 :D:\Code\CSharpProjects\HelloCSharp\Computer\bin\Debug\net8.0\USB

//  获取文件夹下的全部文件
var dllFiles = Directory.GetFiles(USBInterface);

var deviceList = new List<IUSB>();

/*
类库编译生成dll

 文件夹下获取到的文件包括 : 
 
D:\Code\CSharpProjects\HelloCSharp\Computer\bin\Debug\net8.0\USB\FlashDisk.dll
 D:\Code\CSharpProjects\HelloCSharp\Computer\bin\Debug\net8.0\USB\SSD.dll
 */
foreach (var dll in dllFiles)
{
    Console.WriteLine("dllFiles :: "+dll);
    var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(dll);
    /*
     FlashDisk, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
     SSD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
     */
    Console.WriteLine("assembly :: "+assembly);

    var typeList = assembly.GetTypes(); // 获取:命名空间.类型
    foreach (var type in typeList)
    {
        /*
         FlashDisk.FlashDisk
         SSD.SSD
         */
        Console.WriteLine("typeList :: "+type);
        var interfacesList = type.GetInterfaces(); // 获取类的接口

        foreach (var i in interfacesList)
        {
            if (i.Name == "IUSB")
            {
                var usb = (IUSB)Activator.CreateInstance(type)!;
                deviceList.Add(usb);
            }
        }
    }
}
// 方法执行
foreach (var usb in deviceList)
{
    Console.WriteLine("------开始执行USB方法:------ ");
    usb.GetInfo();
    usb.Read();
    usb.Write();
}

生成编译DLL文件

在这里插入图片描述
项目名称\bin\Debug\net8.0\USB
在这里插入图片描述

Benchmark 性能基准测试

测试 代码 性能 优化

【压测】基准测试、性能测试、压力测试–Sysbench

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值