CSharp初级篇 1-4 this、索引器、静态、常量以及只读

本文详细介绍了C#中的this关键字、索引器、静态成员(包括静态构造函数、静态字段和静态函数)的概念及使用方法。此外,还探讨了常量与只读字段的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

.NET Core CSharp初级篇 1-4

本节内容为this、索引器、静态、常量以及只读

简介

在之前的课程中,我们谈论过了静态函数和字段的一小部分知识,本节内容中,我们将详细的讲解关于对象操作的例子,以及更加深入的解释面向对象。

常量

常量,顾名思义,就是一直为同一个值的变量,并且值不可以被改变。在C#中,定义一个常量需要使用const关键字声明。常量并不占用内存的空间。在C#程序编译的时候,编译器会把任何使用了该常量替换成这个值。

因为常量并不存储在内存中,因此常量只允许使用内置的数值类型,例如:bool、char、string、enum。并且声明的同时必须对常量进行初始化。

例如我们应用的版本号,通常在应用编译完成之后都是以一个常量存在,也不需要对他进行操作。看下列代码。

public const string Version="v2.1.1"

public string getString(string msg)
{
    return "Copyright@2019" + msg + Version;
}

上述函数代码在编译时,将会变成:

public string getString(string msg)
{
    return "Copyright@2019" + msg + "V2.1.1";
}

因为常量的上述特性,如果Y程序集使用了X程序集中的这个Version常量,如果X修改了该常量为“2.1.2”并重新编译,若Y不重新编译,Y中常量还是“2.1.1”,因为该常量会被直接固化于Y中并用常量值替换变量名。因此需要y进行重新编译才会使用新的常量值。

静态

静态是一个很常用的语法,我们可以在类中构造各种静态成员,例如静态字段、函数等等。再C#中定义静态成员的方法是使用修饰符static,调用的时候只需要使用“类名.成员名”。

在之前的课程中,我顺带提过一次,静态是一个只初始化执行一次,属于全体共有的一个东西,也可以说是该静态成员属于类本体所有,而不是每一个对象所有。我们就从静态构造函数、静态字段、静态函数这三块进行一个详细的讲解。

静态构造函数

我们之前以及对构造函数进行过一个简单的介绍,构造函数是在类被初始化(实例化)的时候调用,并且每创建一个对象就会调用一次构造函数。

而静态构造函数是每一个类型执行一次,也就是这个类型的所有对象公用一个静态构造函数。这区别与普通构造函数的一个对象执行一次。并且对于静态构造函数而言,一个类只允许有一个静态构造函数,并且必须无参。

静态构造函数在你访问一个类型的静态成员的时候,或者实例化一个类型的时候,编译器会自动的调用静态构造函数。

特别的,因为该初始化的构造函数(静态构造函数)属于所有变量共有并且会调用,那么假设该构造函数报错,那么这个类将再程序剩余生命周期内无法再被使用。

静态字段

静态字段也是一样,属于一切成员公有,在任何地方你都可以不实例化类的情况下对静态字段操作。

对于静态字段的初始化,分为两种情况:

  • 假定类存在一个静态构造函数,那么静态字段在静态构造函数被调用的一瞬间就会初始化;
  • 假定不存在静态构造函数,那么静态字段将会被类型被使用之前的一瞬间初始化(最晚执行),或者更早,在运行时的某一时间(并无法确定)被初始化。

静态字段初始化的顺序则与定义时的顺序一致,例如:

class A
{
    public static int X = Y;
    public static int Y = 15;
}
.....
Console.WriteLine("X:{0},Y:{1}",X,Y)

上例中,X,Y的初始化顺序是X先被初始化,此时Y没有初始化,则是0,因此输出是X:0,Y:15。

静态函数

与之前一样,静态函数可以在不实例化类的情况下调用,但是注意,在静态函数中,不允许使用任何非静态的字段。调用的时候直接使用类名.函数名()即可。

静态类

如果一个类,被声明为静态类,那么该类不可以被实例化,也不可以被继承,同时不可以包含非静态成员。非静态类中,可以包含静态成员。

只读

只读用于字段的访问控制,使用readonly关键字,通常情况下也可以使用无set访问器的属性进行实现。

class A
{
    public string test{get;}
    public readonly string _test;
}

静态成员的生命周期

从程序开始初始化到程序结束,因此滥用静态会导致性能问题。

this关键字

在C#中,this关键字表示操作的当前对象,在类里面,如果你使用this关键字,则this指代的是你每次操作对象时的当前对象。特别的,如果你的函数形参名称和字段名相同,并且你需要同时操作这个两个变量,那么你需要使用this关键字去指明你的操作对象,例如:

class A
{
    private string data;
    public string Data{get{return data;}}

    public void SetData(string data)
    {
        //this.data表示是当前对象的字段data
        this.data = data;
    }


}

索引器

在之前的数组操作中,相信大家都发现了数组的访问通过了一个中括号和下标的方式进行访问,这个东西我们称为索引器。但是在类中的索引器可以以任何类型作为索引的key,这使得整个类的拓展性变得很好。

如何去定义一个索引器呢?这里就需要用到我们的this关键字了。定义的方式有点类似我们对于属性的定义

public class A
{
    public double[] arry{get;set;}
    public double this [int index]
    {
        get
        {
            return arry[index];
        }
        set
        {
            arry[index] = value;
        }
    }
}

通过索引器,我们可以自己定义各种不同的索引方式,而不用拘泥于下标访问

习题

1.请问下列代码输出什么?为什么?

class A
{
    public static A a = new A();
    public static int X = 3();
    A()
    {
        Console.WriteLine(X);
    }
}
class Program
{
    static void Main()
    {
        Console.WriteLine(A.X);
    }
}

2.试着使用索引器,写出一个二维数组的索引访问,要求实现倒序访问(即a[0]访问最后一位)

前往Github获取更多本节资料(PPT,实例代码)
如果我的教程帮到了您,希望您动动小手,在GitHub给我一个star

Github

BiliBili主页

WarrenRyan's Blog

博客园

转载于:https://www.cnblogs.com/WarrenRyan/p/11217976.html

#### 一、命名空间与程序结构相关 #### 1. `namespace` - **定义**:用于声明命名空间,是组织代码的容- **作用**:解决类、方法等成员的命名冲突,将相关代码归类(如按模块、功能划分)。 - **语法**:`namespace 命名空间名 { /* 代码 */ }` - 示例 ```csharp namespace MyApp.DataAccess { // 数据访问层命名空间 public class UserRepository { /* 数据库操作代码 */ } } ``` - **注意**:命名空间可嵌套(如`namespace A.B.C`),访问时需用完整路径(或`using`导入)。 #### 2. `internal`(访问修饰符) - **定义**:指定成员(类、方法、字段等)仅在**同一程序集(Assembly)** 内可见。 - **适用场景**:需在项目内部共享,但不对外暴露的代码(如内部工具类)。 - 示例 ```csharp internal class InternalHelper { // 仅当前程序集可访问 internal void DoWork() { /* 内部逻辑 */ } } ``` #### 3. `class` - **定义**:用于声明类,是面向对象编程的基本单位,封装数据(字段 / 属性)和行为(方法)。 - **特性**:类是对象的模板,可通过`new`创建实例;支持继承、多态等特性。 - 示例 ```csharp public class Person { // 定义"人"类 public string Name { get; set; } // 属性(数据) public void SayHello() { Console.WriteLine($"Hello, {Name}"); } // 方法(行为) } // 创建实例 Person p = new Person { Name = "张三" }; p.SayHello(); // 输出:Hello, 张三 ``` #### 4. `static` - **定义**:用于修饰类、方法、字段等,标识其为 “静态成员”,属于类本身而非实例。 - 特性 - 静态成员不依赖对象实例,可直接通过`类名.成员`访问; - 静态类不能实例化,仅包含静态成员(如工具类)。 - 示例 ```csharp public static class MathTool { // 静态类 public static int Add(int a, int b) => a + b; // 静态方法 } // 调用:无需实例化 int sum = MathTool.Add(2, 3); // 结果:5 ``` #### 5. `void` - **定义**:用于方法返回值声明,表示方法**无返回值**。 - 示例 ```csharp public void PrintMessage() { // 无返回值方法 Console.WriteLine("这是一个void方法"); } ``` #### 6. `const`(常量- **定义**:声明编译时常量,值在编译时确定,且**不可修改**。 - 特性 - 必须在声明时初始化; - 默认静态,可通过`类名.常量名`访问; - 仅支持值类型(如 int、string 字面量)。 - 示例 ```csharp public class Constants { public const double Pi = 3.14159; // 编译时确定 public const string AppName = "MyApp"; } ``` #### 7. `readonly`(只读字段) - **定义**:声明运行时常量,值可在声明时或**构造函数中初始化**,初始化后不可修改。 - 特性 - 支持运行时动态赋值(如从配置文件读取); -静态`readonly`字段依赖实例,静态`readonly`字段属于类。 - 示例 ```csharp public class User { public readonly string Id; // 实例只读字段 public static readonly string Version; // 静态只读字段 static User() { // 静态构造函数初始化 Version = "1.0.0"; } public User(string id) { // 实例构造函数初始化 Id = id; } } ``` ### **二、类型与转换相关** #### 1. 基本类型(sbyte/byte/int/long 等) - 定义:C# 内置的值类型,用于存储数值、字符等数据,按范围和精度划分: - 整数类型:`sbyte`(8 位有符号)、`byte`(8 位无符号)、`short`(16 位)、`int`(32 位)、`long`(64 位)等; - 浮点类型:`float`(32 位单精度)、`double`(64 位双精度)、`decimal`(高精度 decimal,适合货币); - 字符类型:`char`(16 位 Unicode 字符)。 - 示例 ```csharp int age = 25; // 整数 double weight = 62.5; // 双精度浮点 decimal price = 99.99m; // 高精度(需加m后缀) char gender = '男'; // 字符 ``` #### 2. `string` - **定义**:引用类型,用于存储字符串(字符序列),**不可变**(修改时会创建新对象)。 - 核心特性 - 支持`+`拼接(但频繁拼接效率低,推荐`StringBuilder`); - 可通过索引访问单个字符(如`str[0]`); - 常用方法见 “字符串处理” 分类。 #### 3. `object` - **定义**:所有类型的基类(包括值类型、引用类型),任何类型都可隐式转换为`object`。 - 特性: - 用于存储任意类型数据(如集合中混合存储不同类型); - 包含`ToString()`、`Equals()`、`GetHashCode()`等基础方法。 - 示例 ```csharp object obj1 = 123; // int隐式转换为object(装箱) object obj2 = "hello"; // string隐式转换为object ``` #### 4. `var` - **定义**:隐式类型关键字,编译根据赋值自动推断变量类型(仅用于局部变量)。 - 特性: - 简化代码(无需显式声明类型); - 类型在编译时确定,仍为强类型(非动态)。 - 示例 ```csharp var list = new List<int>(); // 推断为List<int> var name = "张三"; // 推断为string ``` #### 5. `dynamic` - **定义**:动态类型,编译时不检查类型,运行时动态解析(类似弱类型)。 - **适用场景**:处理动态数据(如 JSON 反序列化、COM 交互)。 - **注意**:牺牲编译时类型安全,可能导致运行时错误。 - 示例 ```csharp dynamic data = "test"; data = 123; // 运行时允许变更类型 Console.WriteLine(data * 2); // 运行时解析为123*2=246 ``` #### 6. 显式类型转换 `(type)` - **定义**:强制将一种类型转换为另一种类型(需手动指定目标类型)。 - **适用场景**:从大范围类型转为小范围类型(可能丢失数据),如`double`→`int`。 - 示例 ```csharp double x = 3.14; int y = (int)x; // 显式转换,结果为3(丢失小数部分) ``` ### **三、运算符相关** #### 1. 算术运算符(`+ - * / %`) - 作用:执行数值计算: - `+`:加法(或字符串拼接); - `-`:减法; - `*`:乘法; - `/`:除法(整数除法会截断小数); - `%`:取模(求余数)。 - 示例 ```csharp int a = 10, b = 3; Console.WriteLine(a / b); // 3(整数除法) Console.WriteLine(a % b); // 110 ÷ 3 余数为1) ``` #### 2. 自增 / 自减(`++ --`) - 作用:对变量值加 1 或减 1,分前缀( ``` ++x ``` )和后缀( ``` x++ ``` ): - 前缀:先修改值,再使用; - 后缀:先使用值,再修改。 - 示例 ```csharp int x = 5; Console.WriteLine(x++); // 输出5(先使用,后+1),x变为6 Console.WriteLine(++x); // 输出7(先+1,后使用),x变为7 ``` #### 3. 赋值与复合赋值(`= += -= *= /= %=`) - 作用: - `=`:直接赋值; - 复合赋值:赋值时同时执行运算(简化代码)。 - 示例 ```csharp int a = 5; a += 3; // 等价于a = a + 3 → a=8 a *= 2; // 等价于a = a * 2 → a=16 ``` #### 4. 关系运算符(`== != > < >= <=`) - **作用**:比较两个值的关系,返回`bool`(`true`/`false`)。 - 示例 ```csharp int x = 5, y = 10; bool isGreater = x > y; // false bool isEqual = x == y; // false ``` #### 5. 逻辑运算符(`&& || !`) - 作用:组合或反转布尔表达式: - `&&`(逻辑与):两边都为`true`才返回`true`(短路求值:左边为`false`时右边不执行); - `||`(逻辑或):至少一边为`true`返回`true`(短路求值:左边为`true`时右边不执行); - `!`(逻辑非):反转布尔值。 - 示例 ```csharp bool a = true, b = false; Console.WriteLine(a && b); // false Console.WriteLine(a || b); // true Console.WriteLine(!a); // false ``` ### **四、流程控制相关** #### 1. `if / else if / else` - **作用**:根据条件执行不同代码块。 - 语法 ```csharp if (条件1) { /* 条件1为true时执行 */ } else if (条件2) { /* 条件1为false、条件2为true时执行 */ } else { /* 所有条件为false时执行 */ } ``` - 示例 ```csharp int score = 85; if (score >= 90) Console.WriteLine("优秀"); else if (score >= 60) Console.WriteLine("及格"); else Console.WriteLine("不及格"); // 输出:及格 ``` #### 2. `for` 循环 - **作用**:已知循环次数时使用,通过计数控制循环。 - **语法**:`for (初始化; 条件; 迭代) { /* 循环体 */ }` - 示例 ```csharp for (int i = 0; i < 3; i++) { // 循环3次 Console.WriteLine(i); // 输出:0、1、2 } ``` #### 3. `while` 循环 - **作用**:条件满足时重复执行循环体(先判断条件,再执行)。 - **语法**:`while (条件) { /* 循环体 */ }` - 示例 ```csharp int i = 0; while (i < 3) { // 条件为true时执行 Console.WriteLine(i); // 0、1、2 i++; } ``` #### 4. `do-while` 循环 - **作用**:至少执行一次循环体,再判断条件(先执行,后判断)。 - **语法**:`do { /* 循环体 */ } while (条件);` - 示例 ```csharp int i = 3; do { Console.WriteLine(i); // 输出3(即使条件不满足,仍执行一次) i++; } while (i < 3); ``` #### 5. `foreach` 循环 - **作用**:遍历集合或数组中的每个元素(无需手动控制索引)。 - **语法**:`foreach (元素类型 变量名 in 集合) { /* 循环体 */ }` - 示例 ```csharp string[] fruits = { "苹果", "香蕉", "橙子" }; foreach (string fruit in fruits) { Console.WriteLine(fruit); // 依次输出数组元素 } ``` #### 6. `break / continue` - **`break`**:立即跳出当前循环(不再执行后续迭代); - **`continue`**:跳过当前迭代的剩余代码,直接进入下一次迭代。 - 示例 ```csharp for (int i = 0; i < 5; i++) { if (i == 2) break; // 跳出循环 Console.WriteLine(i); // 输出:0、1 } for (int i = 0; i < 5; i++) { if (i == 2) continue; // 跳过当前迭代 Console.WriteLine(i); // 输出:0、1、3、4 } ``` ### **五、字符串处理相关** #### 1. `string.Format` - **定义**:将变量、数值等插入字符串的指定位置,生成格式化字符串。 - 参数: - 第一个参数:格式字符串(含占位符`{0}, {1}...`); - 后续参数:替换占位符的实际值。 - **返回值**:格式化后的新字符串。 - 示例 ```csharp string name = "张三"; int age = 20; string msg = string.Format("姓名:{0},年龄:{1}", name, age); // 结果:"姓名:张三,年龄:20" ``` #### 2. `string.Equals` - **定义**:比较两个字符串的**内容**是否完全一致(区分大小写)。 - **参数**:两个待比较的字符串。 - **返回值**:`bool`(`true`表示内容相同)。 - 示例 ```csharp bool isSame = string.Equals("abc", "ABC"); // false(区分大小写) bool isSame2 = string.Equals("abc", "abc"); // true ``` #### 3. `string.IndexOf` - **定义**:查找子字符串或字符在当前字符串中**首次出现的索引**(从 0 开始)。 - **参数**:待查找的子串或字符。 - **返回值**:索引(`int`),未找到返回`-1`。 - 示例 ```csharp string str = "hello world"; int index = str.IndexOf("world"); // 6("world"从索引6开始) int notFound = str.IndexOf("xyz"); // -1 ``` #### 4. `string.Contains` - **定义**:判断当前字符串是否**包含指定子字符串**。 - **参数**:待查找的子串。 - **返回值**:`bool`(`true`表示包含)。 - 示例 ```csharp bool hasHello = "hello world".Contains("hello"); // true bool hasAbc = "hello world".Contains("abc"); // false ``` #### 5. `string.Replace` - **定义**:将字符串中所有指定的 “旧子串” 替换为 “新子串”,返回新字符串(原字符串不变,因 string 不可变)。 - **参数**:旧子串、新子串。 - **返回值**:替换后的新字符串。 - 示例 ```csharp string str = "hello world"; string newStr = str.Replace("world", "C#"); // 结果:"hello C#" ``` #### 6. `string.ToUpper / ToLower` - **定义**:将字符串转换为全大写(`ToUpper`)或全小写(`ToLower`)。 - **返回值**:转换后的新字符串。 - 示例 ```csharp string str = "Hello World"; string upper = str.ToUpper(); // "HELLO WORLD" string lower = str.ToLower(); // "hello world" ``` #### 7. `StringBuilder` - **定义**:`System.Text`命名空间下的可变字符串类,用于高效拼接、修改字符串(避免`string`不可变导致的性能损耗)。 - 核心方法: - `Append(str)`:追加内容; - `Insert(index, str)`:在指定索引插入内容; - `ToString()`:转换为`string`类型。 - 示例 ```csharp var sb = new StringBuilder(); sb.Append("a"); sb.Append("b"); sb.Insert(0, "prefix_"); // 在开头插入 string result = sb.ToString(); // "prefix_ab" ``` ### **六、数组与集合相关** #### 1. 数组方法(`Array.Sort / Reverse / IndexOf / Clear`) - **`Array.Sort`**:对数组元素排序(默认升序)。 ```csharp int[] arr = {3, 1, 2}; Array.Sort(arr); // 结果:{1, 2, 3} ``` - **`Array.Reverse`**:反转数组元素顺序。 ```csharp int[] arr = {1, 2, 3}; Array.Reverse(arr); // 结果:{3, 2, 1} ``` - **`Array.IndexOf`**:查找元素在数组中首次出现的索引(未找到返回`-1`)。 ```csharp int[] arr = {1, 2, 3}; int index = Array.IndexOf(arr, 2); // 1 ``` - **`Array.Clear`**:清空数组元素(设置为默认值,如`0`、`null`)。 ```csharp int[] arr = {1, 2, 3}; Array.Clear(arr, 0, arr.Length); // 结果:{0, 0, 0} ``` #### 2. 数组条件查找(`Array.Find / FindAll / Exists`) - **`Array.Find`**:返回数组中**第一个满足条件**的元素。 ```csharp int[] nums = {1, 2, 3, 4}; int firstEven = Array.Find(nums, x => x % 2 == 0); // 2 ``` - **`Array.FindAll`**:返回数组中**所有满足条件**的元素(数组)。 ```csharp int[] evens = Array.FindAll(nums, x => x % 2 == 0); // {2, 4} ``` - **`Array.Exists`**:判断数组中**是否存在满足条件**的元素(返回`bool`)。 ```csharp bool hasOdd = Array.Exists(nums, x => x % 2 != 0); // true ``` #### 3. `List`(泛型列表) - **定义**:动态大小的泛型集合,支持自动扩容,比数组更灵活。 - 核心方法: - `Add(item)`:添加元素; - `Remove(item)`:移除指定元素; - `Insert(index, item)`:在指定位置插入元素; - `Contains(item)`:判断是否包含元素; - `Count`:获取元素数量。 - 示例 ```csharp var list = new List<string>(); list.Add("苹果"); list.Add("香蕉"); list.Remove("苹果"); // 结果:{"香蕉"} ``` #### 4. `Dictionary`(字典) - **定义**:键值对集合,通过键(`TKey`)快速查找值(`TValue`),键唯一。 - 核心方法: - `Add(key, value)`:添加键值对; - `Remove(key)`:移除指定键的键值对; - `ContainsKey(key)`:判断键是否存在; - `TryGetValue(key, out value)`:安全获取值(避免键不存在时抛异常)。 - 示例 ```csharp var dict = new Dictionary<int, string>(); dict.Add(1, "苹果"); dict.Add(2, "香蕉"); string fruit = dict[1]; // "苹果" bool hasKey = dict.ContainsKey(3); // false ``` #### 5. `ArrayList`(非泛型动态数组) - **定义**:可存储任意类型元素的动态数组(非泛型,需手动类型转换)。 - **核心方法**:与`List`类似(`Add`、`Remove`、`Insert`等),但元素类型为`object`。 - **注意**:因非泛型,存在装箱 / 拆箱开销,推荐优先使用`List`。 - 示例 ```csharp var list = new ArrayList(); list.Add(123); // 装箱(int→object) list.Add("hello"); int num = (int)list[0]; // 拆箱(object→int) ``` ### **七、面向对象相关** #### 1. 访问修饰符(`public / private / protected / internal`) - **`public`**:全局可见(任何位置可访问); - **`private`**:仅当前类内部可见; - **`protected`**:当前类及派生类可见; - **`internal`**:同一程序集可见。 - 示例 ```csharp public class Person { public string Name; // 全局可见 private int age; // 仅Person类内部可见 protected string Id; // Person及派生类可见 } ``` #### 2. `this` 关键字 - 定义:指代当前类的实例对象,用于: - 区分同名的字段和参数; - 调用当前类的其他构造函数(`this(参数)`); - 作为参数传递当前实例。 - 示例 ```csharp public class Car { private string name; public Car(string name) { this.name = name; // 区分字段和参数 } public void Print() { Console.WriteLine(this.name); // 访问当前实例的字段 } } ``` #### 3. `base` 关键字 - 定义:指代当前类的基类(父类)实例,用于: - 调用父类的构造函数(`base(参数)`); - 访问父类的成员(方法、属性等)。 - 示例 ```csharp public class Animal { public void Eat() { Console.WriteLine("动物进食"); } } public class Dog : Animal { public void Eat(string food) { base.Eat(); // 调用父类的Eat方法 Console.WriteLine($"狗吃{food}"); } } ``` #### 4. `virtual / override`(虚方法与重写) - **`virtual`**:在父类中标记方法为 “可被重写”; - **`override`**:在子类中重写父类的虚方法,实现多态。 - 示例 ```csharp public class Animal { public virtual void MakeSound() { Console.WriteLine("动物叫"); } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("汪汪叫"); } // 重写 } // 多态调用 Animal animal = new Dog(); animal.MakeSound(); // 输出:汪汪叫(运行时调用子类重写的方法) ``` #### 5. `abstract`(抽象类 / 方法) - **抽象类**:用`abstract`修饰,不能实例化,可包含抽象方法和具体方法; - **抽象方法**:用`abstract`修饰,无方法体,强制子类实现。 - 示例 ```csharp public abstract class Shape { // 抽象类 public abstract double GetArea(); // 抽象方法(无实现) } public class Circle : Shape { public double Radius; public override double GetArea() { // 必须实现抽象方法 return Math.PI * Radius * Radius; } } ``` #### 6. `sealed`(密封) - 作用: - 修饰类:禁止被继承; - 修饰方法:禁止子类重写(需配合`override`使用)。 - 示例 ```csharp public sealed class SealedClass { } // 不能被继承 public class Base { public virtual void DoWork() { } } public class Derived : Base { public sealed override void DoWork() { } // 禁止子类重写 } ``` ### **八、委托与事件相关** #### 1. `delegate`(委托) - **定义**:引用类型,封装具有相同签名的方法(类似 “函数指针”,但类型安全)。 - **作用**:将方法作为参数传递,实现回调、事件等。 - 示例 ```csharp // 声明委托类型(无返回值,接受string参数) delegate void PrintDelegate(string msg); // 定义匹配签名的方法 static void Print(string s) { Console.WriteLine(s); } // 委托绑定方法并调用 PrintDelegate print = Print; print("Hello 委托"); // 输出:Hello 委托 ``` #### 2. 系统委托(`Action / Func / Predicate`) - **`Action`**:无返回值的委托(支持 0-16 个参数)。 ```csharp Action<string> log = (msg) => Console.WriteLine(msg); log("日志信息"); ``` - **`Func`**:有返回值的委托(最后一个泛型参数为返回类型)。 ```csharp Func<int, int, int> add = (a, b) => a + b; int sum = add(2, 3); // 5 ``` - **`Predicate`**:返回`bool`的委托(用于条件判断)。 ```csharp Predicate<int> isEven = x => x % 2 == 0; bool result = isEven(4); // true ``` #### 3. 多播委托(`+= / -=`) - **定义**:一个委托实例可绑定多个方法(通过`+=`添加,`-=`移除),调用时按顺序执行所有方法。 - 示例 ```csharp Action action = () => Console.Write("A"); action += () => Console.Write("B"); // 添加第二个方法 action(); // 输出:AB action -= () => Console.Write("A"); // 移除第一个方法 action(); // 输出:B ``` #### 4. `event`(事件) - **定义**:基于委托的封装,用于发布 - 订阅模式(如按钮点击事件)。 - **特性**:仅允许通过`+=`订阅、`-=`取消订阅,禁止外部直接调用。 - 示例 ```csharp public class Button { public event Action OnClick; // 声明事件 public void Click() { OnClick?.Invoke(); // 触发事件(安全调用) } } // 订阅事件 var btn = new Button(); btn.OnClick += () => Console.WriteLine("按钮被点击"); btn.Click(); // 输出:按钮被点击 ``` ### **九、异步与并发相关** #### 1. `async / await` - **`async`**:修饰方法,标记其包含异步操作(返回`Task`/`Task`); - **`await`**:等待异步操作完成,不阻塞当前线程(仅在`async`方法中使用)。 - 示例 ```csharp public async Task<int> GetDataAsync() { // 模拟耗时操作(如网络请求) return await Task.Run(() => { Thread.Sleep(1000); // 模拟耗时 return 100; }); } // 调用 int result = await GetDataAsync(); // 等待结果,不阻塞主线程 ``` #### 2. `Task` - **定义**:表示异步操作的单元,用于替代传统的`Thread`,更高效。 - 核心方法 : - `Task.Run(action)`:在线程池执行异步操作; - `Wait()`:阻塞等待任务完成; - `Result`:获取返回值(阻塞); - `ContinueWith()`:任务完成后执行后续操作。 - 示例 ```csharp Task<int> task = Task.Run(() => 1 + 2); task.ContinueWith(t => Console.WriteLine(t.Result)); // 输出3 ``` #### 3. `lock` - **定义**:确保同一时间只有一个线程执行锁定块内的代码,解决多线程共享资源竞争问题。 - 示例 ```csharp private int count = 0; private object lockObj = new object(); // 锁对象 void Increment() { lock (lockObj) { // 锁定,确保线程安全 count++; } } ``` ### **十、文件操作相关** #### 1. `File` 类方法(`Create / WriteAllText / ReadAllText`) - **`File.Create(path)`**:创建文件,返回`FileStream`(需手动关闭或用`using`)。 - **`File.WriteAllText(path, content)`**:一次性写入文本(自动创建文件,覆盖原有内容)。 - **`File.ReadAllText(path)`**:一次性读取文件所有文本。 - 示例 ```csharp string path = "test.txt"; File.WriteAllText(path, "Hello File"); // 写入 string content = File.ReadAllText(path); // 读取:"Hello File" ``` #### 2. `StreamReader / StreamWriter` - **`StreamReader`**:读取文本文件(支持逐行读取)。 ```csharp using (var sr = new StreamReader("test.txt")) { string line; while ((line = sr.ReadLine()) != null) { // 逐行读取 Console.WriteLine(line); } } ``` - **`StreamWriter`**:写入文本文件(支持追加)。 ```csharp using (var sw = new StreamWriter("test.txt", true)) { // true:追加模式 sw.WriteLine("追加一行"); } ``` #### 3. `Path.Combine` - **定义**:安全拼接路径(自动处理不同系统的路径分隔符,如`\`或`/`)。 - 示例 ```csharp string path = Path.Combine("C:", "Users", "test.txt"); // Windows:"C:\Users\test.txt";Linux:"C:/Users/test.txt" ```
最新发布
08-06
<think>我们正在处理一个WindowsForms应用程序,用户想要设置背景颜色为特定的BGR值(注意:在GDI+中,颜色通常表示为ARGB,而BGR顺序是OpenCV中常用的)。在C#的System.Drawing中,颜色是用Color结构表示的,它使用ARGB(Alpha,Red,Green,Blue)格式。但是,用户指定的是BGR值(即Blue、Green、Red),因此我们需要将BGR转换为Color结构。步骤:1.理解BGR值:用户可能提供一个包含三个整数的值,分别代表蓝色、绿色和红色分量(每个分量通常在0-255之间)。2.转换:由于Color结构需要RGB(即红、绿、蓝)顺序,我们需要将BGR转换为RGB,即交换R和B的位置。-例如,如果用户给出BGR值(b,g,r),那么对应的RGB值就是(r,g,b)。3.设置背景颜色:在WindowsForms中,我们可以通过设置控件的BackColor属性来实现。示例:假设用户想要设置背景颜色为BGR值(0,0,255)——这是一个纯红色(因为蓝色和绿色为0,红色为255)。但在BGR顺序中,这个值表示蓝色分量为0,绿色分量为0,红色分量为255。实际上,这个颜色是红色。但是注意,在BGR顺序中,通常第一个分量是蓝色,第二个是绿色,第三个是红色。所以(0,0,255)在BGR中表示红色,而在RGB中,红色表示为(255,0,0)。因此,我们需要将BGR(0,0,255)转换为RGB(255,0,0)。然而,在OpenCV中,读取图像的颜色通道顺序是BGR,而System.Drawing使用的是RGB。所以当我们从OpenCV得到一个颜色值(BGR顺序)并想在System.Drawing中使用时,需要交换红色和蓝色通道。具体方法:1.提取B、G、R分量。2.使用Color.FromArgb方法,参数顺序是:红色、绿色、蓝色。所以我们将BGR中的R分量作为红色,G分量作为绿色,B分量作为蓝色?不对,实际上:-我们有一个BGR颜色:B,G,R-在RGB中,我们需要:R,G,B所以,转换后的RGB颜色为:R分量(原BGR的第三个分量),G分量(原BGR的第二个分量),B分量(原BGR的第一个分量)?不对,这样会变成(R,G,B)=(原R,原G,原B)。但实际上,BGR顺序是:第一个是蓝色,第二个是绿色,第三个是红色。所以:-原BGR值:第一个数值是蓝色,第二个是绿色,第三个是红色。-转换为RGB:我们需要红色(原BGR的第三个值)、绿色(原BGR的第二个值)、蓝色(原BGR的第一个值)。因此,转换方法为:intr=bgr[2];//红色分量intg=bgr[1];//绿色分量intb=bgr[0];//蓝色分量然后使用Color.FromArgb(r,g,b)来创建颜色。但是,如果用户给的是三个单独的分量(blue,green,red),那么我们可以直接按顺序提取。示例代码:假设我们有一个BGR颜色,用三个整数表示:blue=0,green=0,red=255(表示红色)。转换为RGB:red=255,green=0,blue=0。在C#中,我们可以这样设置一个按钮的背景色:button1.BackColor=Color.FromArgb(255,0,0);//这是红色但是,如果用户给的是BGR顺序的三个整数,我们可以这样写一个转换方法:另外,注意:Color.FromArgb有多个重载,我们使用Color.FromArgb(intred,intgreen,intblue)这个重载。因此,我们可以编写一个辅助方法将BGR转换为Color:```csharppublicstaticColorBgrToColor(intblue,intgreen,intred){//由于BGR顺序,输入参数是blue,green,red//转换为RGB:使用red,green,blue(注意:这里blue是输入的blue,red是输入的red,但顺序是红、绿、蓝)//实际上,我们只需要交换第一个和第三个分量returnColor.FromArgb(red,green,blue);}```注意:上面的方法中,参数顺序是blue,green,red,然后我们使用red,green,blue来创建颜色。但是,这样创建的颜色就是RGB(red,green,blue),也就是把输入的red作为红色分量,输入的green作为绿色分量,输入的blue作为蓝色分量。这实际上就是交换了blue和red的位置。另一种常见情况:用户可能有一个OpenCV的Scalar对象(或者一个包含三个值的数组)表示BGR颜色。我们需要提取这三个值并转换。在WindowsForms中设置背景颜色:我们可以直接设置控件的BackColor属性,例如Form、Panel、Button等。示例:将窗体的背景色设置为BGR(0,0,255)(即红色):```csharpthis.BackColor=BgrToColor(0,0,255);//注意:0,0,255分别代表blue,green,red```但是,注意:Color.FromArgb还有带透明度的重载。如果不需要透明度,我们可以使用三个参数的重载。另外,如果用户给的是整数数组,可以这样:```csharpint[]bgr=newint[]{0,0,255};//B=0,G=0,R=255Colorcolor=Color.FromArgb(bgr[2],bgr[1],bgr[0]);```总结:关键是将BGR顺序转换为RGB顺序,即交换第一个分量(蓝色)和第三个分量(红色)。在引用[4]中提到了绿幕技术,其中背景是绿色。如果我们想设置绿色背景,在BGR中绿色的表示是(0,255,0)(蓝色0,绿色255,红色0)。那么转换为Color时,我们使用:Color.FromArgb(0,255,0)//注意:这里已经是RGB顺序,所以是红色0,绿色255,蓝色0->绿色但是,如果我们有一个BGR表示的绿色:blue=0,green=255,red=0,那么转换后就是Color.FromArgb(red=0,green=255,blue=0)->绿色。因此,对于绿色背景,我们可以直接设置:this.BackColor=Color.Green;//或者使用Color.FromArgb(0,255,0)但为了通用性,我们提供从BGR值转换的方法。现在,根据用户的需求,我们给出一个在WindowsForms中设置背景颜色为特定BGR值的方法。步骤:1.获取BGR值(三个整数,分别代表蓝、绿、红)。2.转换为Color对象(交换蓝色和红色分量)。3.将目标控件(如Form、Panel等)的BackColor属性设置为这个颜色。代码示例(在窗体加载事件中设置背景色):```csharpprivatevoidForm1_Load(objectsender,EventArgse){//假设我们要设置背景色为BGR(0,0,255)->红色intblue=0;intgreen=0;intred=255;ColorbgColor=Color.FromArgb(red,green,blue);//注意:这里顺序是红、绿、蓝,所以用red,green,bluethis.BackColor=bgColor;}```或者写一个转换方法:```csharppublicColorConvertBgrToColor(intblue,intgreen,intred){returnColor.FromArgb(red,green,blue);}```然后使用:```csharpthis.BackColor=ConvertBgrToColor(0,0,255);//红色背景```注意:在WindowsForms中,也可以直接使用已知的颜色名称,如Color.Red,但这里用户要求使用特定的BGR值。另外,如果用户希望设置透明背景,那么需要设置Alpha分量。Color.FromArgb还有一个重载可以指定Alpha(透明度)。例如,如果我们要半透明的红色背景(Alpha=128):```csharpColorbgColor=Color.FromArgb(128,red,green,blue);//注意这个重载的参数顺序是:Alpha,red,green,blue```但是,在BGR值中没有包含Alpha,所以默认不透明(Alpha=255)。如果需要透明度,可以额外指定。最后,如果用户是在处理图像(如引用[1]中在pictureBox中显示图像),那么设置背景颜色可能是设置pictureBox控件的背景色,而不是整个窗体的背景色。方法相同。例如:```csharppictureBox1.BackColor=ConvertBgrToColor(0,0,255);//将pictureBox1的背景设为红色```注意:如果pictureBox中显示图像,背景色可能被图像覆盖,所以需要设置PictureBox的SizeMode或者确保图像有透明区域才能看到背景色。根据引用[1]中的代码,pictureBox1使用的是BackgroundImage,而不是背景色。如果要设置背景色,应该使用BackColor属性,并且确保BackgroundImage有透明部分或者不使用BackgroundImage。因此,用户可能需要同时设置BackColor和BackgroundImageLayout(例如设置为Stretch等)。总结:设置背景颜色为特定BGR值的方法就是将BGR顺序转换为RGB顺序,然后使用Color.FromArgb方法创建颜色,再赋值给控件的BackColor属性。相关问题:1.如何在C#中将BGR颜色值转换为Color结构?2.如何在WindowsForms中设置控件的背景色?3.如何创建带有透明度的颜色?4.在PictureBox中如何同时显示背景色和图像?5.OpenCV中的BGR颜色值如何与System.Drawing.Color进行转换?</think>在 C# 中设置背景颜色为特定 BGR 值的方法主要有以下几种实现方式: ### 1. 使用 `System.Drawing.Color` 结构(推荐) 这是最直接的方法,通过 `Color.FromArgb()` 方法指定 BGR 值: ```csharp // 设置背景颜色为 BGR(0, 0, 255) - 纯红色 int blue = 0; int green = 0; int red = 255; // 创建颜色对象(注意:FromArgb 参数顺序是 Alpha, Red, Green, Blue) Color bgColor = Color.FromArgb(255, red, green, blue); // Alpha=255 表示不透明 // 应用到控件 this.BackColor = bgColor; // 设置窗体背景色 panel1.BackColor = bgColor; // 设置面板背景色 button1.BackColor = bgColor; // 设置按钮背景色 ``` ### 2. 使用 `Color` 的预定义常量 对于常见颜色,可以直接使用预定义常量: ```csharp // 使用预定义颜色 this.BackColor = Color.Red; // 纯红 (BGR:0,0,255) this.BackColor = Color.Green; // 纯绿 (BGR:0,255,0) this.BackColor = Color.Blue; // 纯蓝 (BGR:255,0,0) ``` ### 3. 通过 RGB 值创建颜色 如果需要精确控制 BGR 分量: ```csharp // 直接创建 RGB 颜色 Color customColor = Color.FromArgb( red: 255, // 红色分量 green: 128, // 绿色分量 blue: 0 // 蓝色分量 ); pictureBox1.BackColor = customColor; ``` ### 4. 使用 OpenCV 处理图像背景(高级) 结合 OpenCV 处理图像背景,如引用[4]提到的绿幕技术: ```csharp using OpenCvSharp; // 设置纯色背景 Mat background = new Mat(480, 640, MatType.CV_8UC3, new Scalar(0, 0, 255)); // BGR(0,0,255) 红色 // 显示背景 Cv2.ImShow("Red Background", background); Cv2.WaitKey(0); ``` ### 关键说明: 1. **颜色表示**: - BGR 顺序:Blue(蓝), Green(绿), Red(红) - 每个分量范围:0-255 - 示例:BGR(0,0,255) = 纯红,BGR(0,255,0) = 纯绿 2. **透明度处理**: ```csharp // 半透明红色背景 (Alpha=128) Color semiTransparent = Color.FromArgb(128, 255, 0, 0); this.BackColor = semiTransparent; ``` 3. **颜色空间转换**: 如引用[3]所述,特定场景可能需要转换到 HSV 等颜色空间: ```csharp // 转换到 HSV 空间处理颜色 Mat hsvImage = new Mat(); Cv2.CvtColor(original, hsvImage, ColorConversionCodes.BGR2HSV); ``` ### 实际应用示例 - 绿幕背景替换: ```csharp // 基于引用[4]的绿幕技术实现 Mat ReplaceGreenScreen(Mat frame, Color newBgColor) { // 转换到 HSV 空间 Mat hsv = new Mat(); Cv2.CvtColor(frame, hsv, ColorConversionCodes.BGR2HSV); // 定义绿色范围 (H:35-85, S:100-255, V:50-255) Scalar lowerGreen = new Scalar(35, 100, 50); Scalar upperGreen = new Scalar(85, 255, 255); // 创建绿色掩码 Mat mask = new Mat(); Cv2.InRange(hsv, lowerGreen, upperGreen, mask); // 创建新背景 Mat background = new Mat(frame.Rows, frame.Cols, MatType.CV_8UC3, new Scalar(newBgColor.B, newBgColor.G, newBgColor.R)); // 合成图像 Mat result = new Mat(); Cv2.CopyTo(background, result, mask); return result; } ``` ### 注意事项: 1. **内存管理**:使用 OpenCV 对象后需手动释放资源 2. **性能优化**:处理视频时使用指针操作提升效率 3. **跨平台**:.NET Core 需通过 `System.Drawing.Common` NuGet 包使用颜色功能 > 在图像处理中,BGR 是 OpenCV 的标准顺序,而 .NET 的 `Color` 使用 ARGB 顺序,转换时需注意分量顺序[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值