C#

C#笔记

C#笔记

Posted by jianba on May 29, 2020

C#笔记

数据类型值类型

类型,变量和值

内置类型

引用类型:string,object 引用类型png 值类型:结构体和枚举也属于值类型 值类型png0

类(class)与结构(struct)的异同
1
2
1. Class 可以被实例化,属于引用类型,是分配在内存的堆上的。类是引用传递的
2. Struct 属于值类型,是分配在内存的栈上的。结构体是复制传递的。Int32、Boolean 等都属于结构体
拆箱与装箱:
  • 装箱:从值类型接口转换到引用类型。
  • 拆箱:从引用类型转换到值类型。
1
2
3
4
5
    object obj = null;//引用类型
    obj = 1;//装箱,boxing。把值类型包装为引用类型。
    int i1 = (int)obj;//拆箱。unboxing
补:
   在 inboxing(装箱)时是不需要显式的类型转换的,不过 unboxing(拆箱)需要显式的类型转换
参考
值类型

栈内存运行效率高,但是空间尺寸比较小,栈内存中的数据方法结束后自动释放;值类型在栈内存中

  • 引用类型
  1. 堆内存运行效率较低,但是空间充足,堆内存中不会自动释放,需要 GC 负责释放,引用类型对象在堆内存中。

  2. 引用类型和值类型。 引用类型的变量存储对其数据(对象)的引用,而值类型的变量直接包含其数据。 对于引用类型,两种变量可引用同一对象;因此,对一个变量执行的操作会影响另一个变量所引用的对象。 对于值类型,每个变量都具有其自己的数据副本,对一个变量执行的操作不会影响另一个变量(in、ref 和 out 参数变量除外;请参阅 in、ref 和 out 参数修饰符)。

  • 值类型和引用类型的区别 </br> 1.值类型是拷贝传递,引用类型是引用传递; 2.值类型不可能派生出新的类型:所有的值类型均隐式派生自 System.ValueType 3.值类型不能为 null,引用类型可以为 null
值转换
  • 隐式转换不需要做任何工作,也不需要另外编写代码。
  • 显式转换: 明确要求编译器把数值从一种数据类型转换为另一种数据类型。

概念来自 C# 入门经典第五版.(79)

  • as & is:由于是多态对象,基类类型的变量可以保存派生类型。 要访问派生类型的实例成员,必须将值强制转换回派生类型。 但是,强制转换会引发 InvalidCastException 风险。 C# 提供模式匹配语句,该语句只有在成功时才会有条件地执行强制转换。 C# 还提供 is 和 as 运算符来测试值是否属于特定类型。
    1. as:类型转换。as操作符不会做过的转换操作,当需要转化对象的类型属于转换目标类型或者转换目标类型的派生类型时,那么此转换操作才能成功,而且并不产生新的对象【当不成功的时候,会返回null】。
    2. is 检查对象是否与给定的类型兼容。返回 true false
  • Convert PK Parse
    1. 都是强制类型转换,但是parse只是int的一个静态方法(int.parse())。
    2. 两个方法的最大不同是它们对null值的处理方法。Convert.ToInt32(null)会返回0而不会产生任何异常,但int.Parse(null)则会产生异常。
    3. a. Convert.ToInt32(double value) 如果 value 为两个整数中间的数字,则返回二者中的偶数;即 3.5转换为4,4.5 转换为 4,而 5.5 转换为 6。 不过4.6可以转换为5,4.4转换为4
    4. Parse就是把String转换成int,char,double….等,也就是*.Parse(string) 括号中的一定要是string.
    5. Convert可以提供多种类型的转换,也就是Convert.*()括号中可以为很多种类型(包括string).

参考链接

常用函数

字符串

string

C#-string菜鸟教程

字符串对象是“不可变的” :它们在创建后无法更改。 当 s1 和 s2 的内容被串联在一起以形成单个字符串时,两个原始字符串没有被修改。 += 运算符创建一个新的字符串,其中包含组合的内容。 这个新对象被分配给变量 s1,而分配给 s1 的原始对象被释放,以供垃圾回收,因为没有任何其他变量包含对它的引用。

  • 初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
string message1;

// Initialize to null.
string message2 = null;

// Initialize as an empty string.
// Use the Empty constant instead of the literal "".
string message3 = System.String.Empty;

// Initialize with a regular string literal.
string oldPath = "c:\\Program Files\\Microsoft Visual Studio 8.0";

// Initialize with a verbatim string literal.
string newPath = @"c:\Program Files\Microsoft Visual Studio 9.0";
// 或者使用/来转义

char[] letters = { 'A', 'B', 'C' };
string alphabet = new string(letters);

  • 字符内插
1
2
3
4
5
6
7
8
9
var jh = (firstName: "Jupiter", lastName: "Hammon", born: 1711, published: 1761);
Console.WriteLine($"{jh.firstName} {jh.lastName} was an African American poet born in {jh.born}.");
Console.WriteLine($"He was first published in {jh.published} at the age of {jh.published - jh.born}.");
Console.WriteLine($"He'd be over {Math.Round((2018d - jh.born) / 100d) * 100d} years old today.");

// Output:
// Jupiter Hammon was an African American poet born in 1711.
// He was first published in 1761 at the age of 50.
// He'd be over 300 years old today.

string.format

  • 复制函数

str.clone()返回值不是此实例的独立副本;它只是相同数据的另一个视图。 使用 Copy 或 CopyTo 方法来创建一个与此实例具有相同值的单独 String 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
string.clone();

clone 和 copy 区别 (新版中只有copyto方法,copy已弃用)
Clone()返回值是Object,Copy返回值为void
Clone()是非静态方法(一个接口),Copy为静态方法。
Clone()会创建一个新数组,Copy方法必须传递阶数相同且有足够元素的已有数组。
clone   是传递一个引用,相当于创建了一个指向原字符串的一个指针,copy是复制一个对象。  

public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count);

sourceIndex Int32       要复制的此实例中第一个字符的索引。

destination Char[]      此实例中的字符所复制到的 Unicode 字符数组。

destinationIndex Int32  destination中的索引,在此处开始复制操作。

count Int32             此实例中要复制到 destination 的字符数。

    string strSource = "changed";
    char [] destination = { 'T', 'h', 'e', ' ', 'i', 'n', 'i', 't', 'i', 'a', 'l', ' ',
                'a', 'r', 'r', 'a', 'y' };

        // Print the char array
        Console.WriteLine( destination );

        // Embed the source string in the destination string
        strSource.CopyTo ( 0, destination, 4, strSource.Length );

        // Print the resulting array
        Console.WriteLine( destination );

        strSource = "A different string";

        // Embed only a section of the source string in the destination
        strSource.CopyTo ( 2, destination, 3, 9 );

        // Print the resulting array
        Console.WriteLine( destination );
        
//       The initial array
//       The changed array
//       Thedifferentarray
  • Compare常用

参考文章MSDN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 string s1 = "ab";
 string s2 = "aB";
 Console.WriteLine(string.Compare(s1,s2,true)); // 忽略大小写 0 相等
 
 Console.WriteLine(s1.CompareTo(s2)); // -1 s1 < s2

// String.CompareOrdinal
CompareOrdinal是将整个字符串每5个字符(10个字节)分成一组,然后逐个比较,找到第一个不相同的ASCII码后退出循环。并且求出两者的ASCII码的差 字符串比较时间会更快。

CompareOrdinal(String, Int32, String, Int32, Int32) 	
通过计算每个子字符串中相应 String 对象的数值来比较两个指定的 Char 对象的子字符串。

CompareOrdinal(String, String) 	
通过计算每个字符串中相应 String 对象的数值来比较两个指定的 Char 对象。
    
    String strLow = "abc";
	String strCap = "ABC";
	String result = "equal to ";
	int x = 0;
	int pos = 1;  
    // The Unicode codepoint for 'b' is greater than the codepoint for 'B'.
	x = String.CompareOrdinal(strLow, pos, strCap, pos, 1);
	if (x < 0) result = "less than";
	if (x > 0) result = "greater than";
	Console.WriteLine("CompareOrdinal(\"{0}\"[{2}], \"{1}\"[{2}]):", strLow, strCap, pos);
	Console.WriteLine("   '{0}' is {1} '{2}'", strLow[pos], result, strCap[pos]);

    result = String.CompareOrdinal(str1, str2);

  • String 比较函数(contains)

参考文章-MSDN

  1. Contains(Char, StringComparison) 使用指定的比较规则返回一个值,该值指示指定的字符是否出现在此字符串中。

  2. Contains(Char) 返回一个值,该值指示指定的字符是否出现在此字符串中。

  3. Contains(String) 返回一个值,该值指示指定的子串是否出现在此字符串中。

  4. Contains(String, StringComparison) 使用指定的比较规则返回一个值,该值指示指定的字符串是否出现在此字符串中。

  5. 返回Boolean。 value 参数在此字符串中出现,则为 true;否则为 false。

  6. 怎样忽略大小写

1
2
string title = "STRING";
bool contains = title.IndexOf("string", StringComparison.OrdinalIgnoreCase) >= 0;
  1. string.contains() vs string.indexof()

7.1 内部实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        public bool Contains(string value)
        {
            return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
        }
        public int IndexOf(string value)
        {
            return this.IndexOf(value, StringComparison.CurrentCulture);
        }
StringComparison.Ordinal / OrdinalIgnoreCase
内部按照byte比较很快

StringComparison.CurrentCulture / CurrentCultureIgnoreCase
是在当前的区域信息下进行比较(慢)

StringComarison.InvariantCulture / InvariantCultureIgnoreCase
用于指定特定国家

// https://www.cnblogs.com/zhw511006/archive/2010/07/09/1774591.html
  • string.indexof()
1
2
3
4
5
6
7
8
9
10
11
12
13
IndexOf(String, Int32, Int32) 
报告指定字符串在此实例中的第一个匹配项的从零开始的索引。 搜索从指定字符位置开始,并检查指定数量的字符位置.
IndexOf(String, Int32, Int32, StringComparison) // 添加枚举规则

IndexOf(String, Int32)  
IndexOf(String, Int32, StringComparison) 
报告指定的字符串在当前 String 对象中的第一个匹配项的从零开始的索引。 参数指定当前字符串中的起始搜索位置以及用于指定字符串的搜索类型。

IndexOf(String) 
IndexOf(String, StringComparison) 

// 所有string可替换为char
// new string('a',10)
  • EndsWith;
1
2
3
4
5
6
7
8
9
10
11
12
public bool EndsWith (string value);
public bool EndsWith (char value);
public bool EndsWith(String, StringComparison)
public bool EndsWith(String, Boolean, CultureInfo)

String[] strings = { "This is a string.", "Hello!", "Nothing.", "Yes.", "randomize" };
    foreach (var value in strings) 
    {
      bool endsInPeriod = value.EndsWith(".");
      Console.WriteLine("'{0}' ends in a period: {1}", value, endsInPeriod);
    }

  • String.Equals
1
2
3
4
5
str.Equals(String)
str.Equals(String, StringComparison)
string.Equals(String, String, StringComparison)
string.Equals(String, String)
str.Equals(Object)
  • 格式化输出(format tostring)

参考文章MSDN

  1. 用法1
1
2
3
4
5
Decimal pricePerOunce = 17.36m;
String s = String.Format("The current price is {0} per ounce.",pricePerOunce);
Console.WriteLine(s);
// Result: The current price is 17.36 per ounce.
// {0:C2} => $17.36
  1. 输出间距控制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
            int[] years = { 2013 };
            int[] population = { 1025632};
            var sb = new System.Text.StringBuilder();
            sb.Append(String.Format("{0,6} {1,15}\n\n", "Year", "Population"));
            for (int index = 0; index < years.Length; index++)
                sb.Append(String.Format("{0,6} {1,15:N0}\n", years[index], population[index]));
            Console.WriteLine(sb);

            // Result:
            //      Year      Population
            //
            //      2013       1,025,632
//去除:NO => 1025632
//左对齐 {0,-6}

  1. 日期

https://www.cnblogs.com/arxive/p/6415312.html

日期格式

自定义:

1
2
3
4
5
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff"); // => 2016-05-09 13:09:55:2350

DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:ffff"); // => 2016/05/09 13:09:55:2350

DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:ffff dddd"); // => 2016/05/09 13:09:55:2350 星期
  1. 输出格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  string output = "";
  var date = DateTime.Now;
  output += String.Format("It is {0:t} on {0:d}. The day of the week is {1}.", 
                          date, date.DayOfWeek);
  Console.WriteLine(output); 
  // It is 10:29 AM on 1/8/2018. The day of the week is Monday.

相同效果
$ 内插字符
https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/tokens/interpolated

output += $"\nIt is {date:t} on {date:d}. The day of the week is {date.DayOfWeek}.";
Console.WriteLine(output);

控制小数点后位数

1. 用“0”
 decimal value = 16309.5436m;
 string result = String.Format("{0,12:#.00000} {0,12:0,000.00} {0,12:000.00#}",value);
 Console.WriteLine(result);
 // The example displays the following output:
 //        16309.54360    16,309.54    16309.544

2. 
object[] values = { 1603, 1794.68235, 15436.14 };
string result;
foreach (var value in values) {
   result = String.Format("{0,12:C2}   {0,12:E3}   {0,12:F4}   {0,12:N3}  {1,12:P2}\n",
                          Convert.ToDouble(value), Convert.ToDouble(value) / 10000);
   Console.WriteLine(result);
}                           
// The example displays output like the following:
//       $1,603.00     1.603E+003      1603.0000      1,603.000       16.03 %
//    
//       $1,794.68     1.795E+003      1794.6824      1,794.682       17.95 %
//    
//      $15,436.14     1.544E+004     15436.1400     15,436.140      154.36 %

控制整数位

1. 用“0”
 int value = 16342;
 string result = String.Format("{0,18:00000000} {0,18:00000000.000} {0,18:000,0000,000.0}", value);
 Console.WriteLine(result);
// The example displays the following output:
//           00016342       00016342.000    0,000,016,342.0
2.  
 int value = 1326;
 string result = String.Format("{0,10:D6} {0,10:X8}", value);
 Console.WriteLine(result);
// The example displays the following output:
//     001326   0000052E

浮点数输出控制(有点重复额额额额额)
 1,控制袭值本身就是2位小bai数。
 float money = 123.4567f;
 money = (float)Math.Round(money, 2);
 textBox1.Text = money.ToString();
 2. 控制输出时显示2位小数;
 float money = 123.4567f;
 textBox1.Text = money.ToString("#.##");
 3.  textBox1.Text = money.ToString("0.00"); 

OT
https://www.liujiajia.me/2017/6/26/format-specifiers-in-csharp

1. 货币金额格式字符

C或c用来组成将数据转换为货币金额格式的字符串。
该字符后面的数字表示货币金额数据小数点后保留的数字个数。
int a = 12345;
string str1 = String.Format("{0:c2}",a); // 输出¥12,345.00

2. 整数数据格式

字符D或d用来组成将数据表示为十进制整数数据的格式化字符串,其后面的数字表示数据的位数,
如果这个数字小于整数数据的位数,则显示所有的整数位;
如果这个数字大于整数数据的位数,则在整数数据的前面用数字“0”补足所有的位数。

int a = 12345;
string str1 = String.Format("{0:d6}",a);//012345

3. 科学计数法格式

字符E或e用来组成将数据转换为科学计数法形式,其后面的数字用来规定科学计数法表示数据的小数点后数字的个数。
如果该字符后面没有数字,则显示7位有效数字。

int a = 12345;
string str1 = String.Format("{0:e6}",a); // 1.234500e+004
string str2 = String.Format("{0:e}",a); // 1.234500e+004

4. 浮点数据格式

字符F或f用来描述带有小数点的数据的显示形式,该字符后面的数字规定了小数点后的数据位数,如果没有指定数字,则数据默认保留2位小数。
如果指定数字大于数据本身小数部分的位数,则在小数部分数字的最后补“0”。

int a = 12345;
double d = 12345.55678;
string str1 = String.Format("{0:f}",a); // 12345.00
string str2 = String.Format("{0:f3}",d); // 12345.557

5. 通用数据格式

字符G或g用于将数据格式化为最紧凑的字符格式。该种格式符将根据具体的数据决定是用科学计数法表示,还是用定点数据格式或整数数据格式表示更紧凑,并返回更紧凑的一种格式。

int a = 12345;
double d = 1345.55678;
string str1 = String.Format("{0:g}",a); // 结果为12345
string str2 = String.Format("{0:g}", d); // 结果为1345.55678
string str3 = String.Format("{0:g4}", a); // 结果为1.235e+04
string str4 = String.Format("{0:g4}", d); // 结果为1346

6. 自然数据格式

字符N或n用来表示自然数据格式将数据格式化成带逗号和小数点的形式。

int a = 12345;
double d = 1345.55678;
string str1 = String.Format("{0:n}",a); // 结果为12,345.00
string str2 = String.Format("{0:n}", d); // 结果为1,345.56
string str3 = String.Format("{0:n3}", a); // 结果为12,345.000
string str4 = String.Format("{0:n3}", d); // 结果为1,345.557

7. 十六进制数据格式

字符X或x用于将数据表示为十六进制数据格式,其后面的数字表示格式化数据的数字个数,其规定与整数格式类似。

int a = 123455;
string str1 = String.Format("{0:x}",a); // 1e23f
string str2 = String.Format("{0:x5}", a); // 1e23f
string str3 = String.Format("{0:x6}", a); // 01e23f
string str4 = String.Format("{0:x7}", a); // 001e23f

  • indexOFAny:报告指定 Unicode 字符数组中的任意字符在此实例中第一个匹配项的索引。 如果未在此实例中找到数组中的字符,则此方法返回 -1。 //类似 LastIndexOf() LastIndexOfAny()

  • insert 返回一个新的字符串,在此实例中的指定的索引位置插入指定的字符串。

1
String modified = original.Insert(3, " ");
  • padleft padright
1
2
3
4
5
6
7
8
   string str = "forty-two";
   char pad = '.';
   Console.WriteLine(str.PadLeft(15, pad));
   Console.WriteLine(str.PadLeft(2, pad));

   string str = "BBQ and Slaw";
   Console.WriteLine(str.PadLeft(15));  // Displays "   BBQ and Slaw".
   Console.WriteLine(str.PadLeft(5));   // Displays "BBQ and Slaw".
  • remove
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    string s = "abc---def"; 
    Console.WriteLine("Index: 012345678");
    Console.WriteLine("1)     {0}", s);
    Console.WriteLine("2)     {0}", s.Remove(3));
    Console.WriteLine("3)     {0}", s.Remove(3, 3));
    }
/*
This example produces the following results:

Index: 012345678
1)     abc---def
2)     abc
3)     abcdef

*/
  • replace
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
String s = "aaa";
Console.WriteLine("The initial string: '{0}'", s);
s = s.Replace("a", "b").Replace("b", "c").Replace("c", "d");
Console.WriteLine("The final string: '{0}'", s);
// aaa 
// ddd

String str = "1 2 3 4 5 6 7 8 9";
Console.WriteLine("Original string: \"{0}\"", str);
Console.WriteLine("CSV string:      \"{0}\"", str.Replace(' ', ','));
// 1 2 3 4 5 6 7 8 9
// 1,2,3,4,5,6,7,8,9"

string s1 = "ab";
string s3 = s1.Insert(0, "A");
Console.WriteLine(s3);
string s4 = s3.Replace("a", "C", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(s4);
// CCb
  • split

    就是分割数组

    1. 常规操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
String.Split (Char[])
返回包含此实例中的子字符串(由指定 Char 数组的元素分隔)的 String 数组。
由 .NET Compact Framework 支持。

String.Split (Char[], Int32)
返回包含此实例中的子字符串(由指定 Char 数组的元素分隔)的 String 数组。参数指定返回的子字符串的最大数量。

String.Split (Char[], StringSplitOptions)
返回包含此字符串中的子字符串(由指定的 Char 数组的元素分隔)的 String 数组。参数指定是否返回空数组元素。

String.Split (String[], StringSplitOptions)
返回包含此字符串中的子字符串(由指定的 String 数组的元素分隔)的 String 数组。参数指定是否返回空数组元素。

String.Split (Char[], Int32, StringSplitOptions)
返回包含此字符串中的子字符串(由指定的 Char 数组的元素分隔)的 String 数组。参数指定要返回子字符串的最大数量,以及是否要返回空数组元素。

String.Split (String[], Int32, StringSplitOptions)
返回包含此字符串中的子字符串(由指定的 String 数组的元素分隔)的 String 数组。参数指定要返回子字符串的最大数量,以及是否要返回空数组元素。

// 忽略大小写 只能用正则表达式来做
string[] li = Regex.Split(s3, "a", RegexOptions.IgnoreCase);

// 多个替换
char[] delimiterChars = { ' ', ',', '.', ':', '\t' };

string text = "one\ttwo three:four,five six seven";
System.Console.WriteLine($"Original text: '{text}'");

string[] words = text.Split(delimiterChars);
System.Console.WriteLine($"{words.Length} words in text:");

foreach (var word in words)
{
    System.Console.WriteLine($"<{word}>");
}

//ot 
默认 StringSplitOptions.None 不去除空元素

  1. 数字相关
1
2
3
4
5
6
7
8
9
10
            string s3 = "1aa2a2  3a4  2a     a5A6";
            Console.WriteLine(s3);
            string[] li = s3.Split("a", 2,StringSplitOptions.RemoveEmptyEntries);
            foreach (var item in li)
            {
                Console.WriteLine(item);
            }
            //1
            //2a2  3a4  2a a5A6

  1. 正则表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Regex.Split (String)
在由 Regex 构造函数中指定的正则表达式模式定义的位置拆分指定的输入字符串。
由 .NET Compact Framework 支持。

Regex.Split (String, Int32)
在由 Regex 构造函数中指定的正则表达式定义的位置,将指定的输入字符串拆分指定的最大次数。
由 .NET Compact Framework 支持。

Regex.Split (String, String)
在由正则表达式模式定义的位置拆分输入字符串。
由 .NET Compact Framework 支持。

Regex.Split (String, Int32, Int32)
从输入字符串中的指定字符位置开始,在由 Regex 构造函数中指定的正则表达式定义的位置,将指定的输入字符串拆分指定的最大次数。
由 .NET Compact Framework 支持。

Regex.Split (String, String, RegexOptions) 	在由指定的正则表达式模式定义的位置拆分输入字符串。可指定选项来修改匹配的行为。
由 .NET Compact Framework 支持。

            string s3 = "1a2a3a4  2 a5A6";
            Console.WriteLine(s3);
            string[] li = Regex.Split(s3, "a", RegexOptions.IgnoreCase);

  • substring
  1. Substring(Int32) 从此实例检索子字符串。 子字符串在指定的字符位置开始并一直到该字符串的末尾。

  2. Substring(Int32, Int32) 从此实例检索子字符串。 子字符串从指定的字符位置开始且具有指定的长度。

  • tochararray
  1. ToCharArray(Int32, Int32) 将此实例中的指定子字符串内的字符复制到 Unicode 字符数组。
1
2
3
4
5
6
7
8
9
10
11
12
string s3 = "00012345678";
            Console.WriteLine(s3);
            char[] li = s3.ToCharArray(2, 4);
            foreach (var item in li)
            {
                Console.WriteLine(item);
            }
           //00012345678
          // 0
          //1
          //2
          //3
  1. ToCharArray() 将此实例中的字符复制到 Unicode 字符数组。
  • trim trimend trimstart // 去除空格
  • 进制转换

stringbuilder

list | array | arrylist

List 是针对特定类型、任意长度的。 Array 是针对任意类型、固定长度的。 ArrayList 是针对任意类型、任意长度的。 Array 和 ArrayList 是通过存储 object 实现任意类型的,所以使用时要转换

  • list
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    
    // 初始化
              var tList = new List<string>();
              string addStr = "123";
    //添加元素
              tList.Add(addStr);  //添加到尾部一个元素
              tList.Add(addStr);
    
              string[] strArray =  { "3456","567333333333" };
              tList.AddRange(strArray); //添加到尾部一组元素
                
              tList.Insert(0, addStr);  //插入指定位置元素
    //删除元素
              tList.Remove(addStr); // 删除从下表为零开始匹配到的第一个元素
              tList.RemoveAt(0);  //删除指定位置元素
              tList.RemoveRange(0, 1);  //从下标index (0)开始,删除count(1)个元素
            //tList.Clear(); // 清空
    //判断元素是否存在
              Console.WriteLine(tList.Contains("5673333")); // 判断某个元素是否在该List中(返回值为:true/false)
    // sort
              tList.Sort();  // 给List里面元素排序(默认是元素第一个字母按升序)
    // 反转
              tList.Reverse(); // 反转
              //Reverse(Int32, Int32)
    // 查找元素下标
              Console.WriteLine(tList.IndexOf("3456"));
              // 复制
              var tList2 = new string[15];
              tList.CopyTo(tList2);
    // 进阶方法
             //使用lambda表达式(委托)
              var ans = tList.Find(name =>
              {
                  if (name.Contains('3'))
                      return true;
                  else
                      return false;
              });
    
              var ans2 = tList.FindAll(name =>
              {
                  if (name.Contains('3'))
                      return true;
                  else
                      return false;
              });
    

dictionary | hashtabel

都不可以存重复元素。 Hashtable中key-value键值对均为object类型,所以Hashtable可以支持任何类型的keyvalue键值对,任何非 null 对象都可以用作键或值。 Dictionary表示键和值的集合,支持泛型,hashtable不可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
            var map = new Dictionary<string, string>();
            map.Add( "a", "1232" );
            Console.WriteLine(map["a"]);  //  1232
            Console.WriteLine(map.ContainsKey("a"));  // true
            Console.WriteLine(map.ContainsValue("1232"));  // true
            //map.Remove("a");
            foreach (var entry in map)
            {
                Console.Write(entry.Key + ":");
                Console.WriteLine(entry.Value);
            } // a:1232
            
            var mmp = new Hashtable();
            string? aa = "1221";
            mmp.Add(aa,"12323");
            //mmp.Add(aa, "88848");
            foreach(var item in mmp.Keys)
            {
                Console.Write(item.ToString() + ":");
                Console.WriteLine(mmp[item.ToString()]);
            }// 1221:12323

viewdata viewbag

  • ViewData是字典类型,赋值方式以键值对的形式,通过key值读取对应的value,ViewData[“myName”]。

  • ViewBag是动态类型,使用时直接添加属性赋值点即可 ViewBag.myName。

  • ViewBag和ViewData只在当前Action中有效,等同于View。

  • ViewData和ViewBag中的值可以互相访问,因为ViewBag的实现中包含了ViewData,ViewBag是对ViewData一个动态封装。

ViewData ViewBag
它是key/value字典集合 它是dynamic类型对象
从asp.net mvc1就有了 从asp.netmvc3才有
基于asp.netframework 3.5 基于asp.net framework4.0
viewdata比viewbag快 viewbag比viewdata慢
页面查询数据时需要转换合适的类型 在页面查询数据时不需要转换合适的类型
有一些类型转换代码 可读性较好
1
2
3
4
5
6
7
8
9
        public ActionResult Index()
        {
            ViewData["nameTest"] = "Hella World";
            ViewBag.nameTest= "Hella World";
            return View();
        }

<span>@ViewData["name"]</span>
<span>@ViewBag.nass</span>

hashset

1
2
3
4
5
6
7
8
9
10
11
12
13
 var iSet = new HashSet<int>();
            iSet.Add(123);
            //iSet.Clear(); // 清空
            //iSet.TrimExcess(); //主动释放内存 o(n)操作
            //iSet.Remove(123);
            Console.WriteLine(iSet.Contains(123));
            var arry = new int[1];
            iSet.CopyTo(arry);
            Console.WriteLine(arry[0]);  //123
            //CopyTo(T[], Int32)
            //从指定数组索引处开始,将 HashSet<T> 对象的元素复制到数组中。
            // CopyTo(T[], Int32, Int32)
            //从指定数组索引处开始,将 HashSet< T > 对象的指定数目的元素复制到数组中。

DataTable | dataset

存数据库用的

复制

C#入门经典201页实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace Ch09Ex03 {     
    class MyClass {public int val;}
    struct myStruct {public int val;}     
    class Program {
        static void Main(string[] args)
        {            
           MyClass objectA = new MyClass();
           MyClass objectB = objectA;             
           objectA.val = 10;             
           objectB.val = 20;             
           myStruct structA = new myStruct();             
           myStruct structB = structA;             
           structA.val = 30;             
           structB.val = 40;             
           Console.WriteLine("objectA.val = {0}", objectA.val);
           Console.WriteLine("objectB.val = {0}", objectB.val); 
           Console.WriteLine("structA.val = {0}", structA.val); 
           Console.WriteLine("structB.val = {0}", structB.val);
           Console.ReadKey();
           // 20 20 30 40
        }     
    } 
}
  • 示例说明
    1. 声明类型的变量。
    2. 在这个变量中创建该类型的新实例。
    3. 声明类型的第二个变量。
    4. 把第一个变量赋给第二个变量。
    5. 在第一个变量的实例中,给val字段赋一个值。
    6. 在第二个变量的实例中,给val字段赋一个值。
    7. 显示两个变量的val字段值。
  • 为什么回出现输出值不相同
  1. 对象是引用类型。在把对象赋给变量时,实际上是把带有一个指针的变量赋结了该指针所指向的对象。在实际代码中,指针是内存中的一个地址。在这种情况下,地址是内存中该对象所在的位置。在用下面的代码行把第一个对象引用赋给类型为MyClass的第二个变量时,实际上是复制了这个地址。
    MyClass objectB = objectA; 这样两个变量就包含同一个对象的指针。

  2. 结构是值类型。其变量并不是包含结构的指针,而是包含结构本身。在用下面的代码把第一个结构赋给类型为myStruct的第二个变量时,实际上是把第一个结构的所有信息复制到另一个结构中。
    myStruct structB = structA; 这个过程与本书前面介绍的简单变量类型如int是一样的。最终的结果是两个结构类型变量包含不同的结构。使用指针的全部技术隐藏在托管C#代码中,它使得代码更简单。使用C#中的不安全代码可以进行低级操作,如指针操作,但这是一个比较高级的论题,这里不予以讨论。

浅复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class People
    {
        public int _age;
        public string _name;
        public People(int Age, string Name)
        {
            _age = Age;
            _name = Name;
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            People Mike = new People(12, "Mike");
            People Mike2 = Mike;
            Mike._age = 0;
            Console.WriteLine("{0}  {1}", Mike._age.ToString(), Mike._name);
            Console.WriteLine("{0}  {1}", Mike2._age.ToString(), Mike2._name);
        }
        // 0  Mike
        // 0  Mike
    }
深复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class People
    {
        public int _age;
        public string _name;
        public People(int Age, string Name)
        {
            _age = Age;
            _name = Name;
        }
        public object Clone() // clone就是一个接口
        {
            People MySelf = new People(this._age, this._name);
            return MySelf;
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            People Mike = new People(12, "Mike");
            People Mike2 = Mike;
            Mike._age = 0;
            People mike3 = Mike.Clone() as People;
            mike3._age = 11111;
            Console.WriteLine("{0}  {1}", Mike._age.ToString(), Mike._name);
            Console.WriteLine("{0}  {1}", Mike2._age.ToString(), Mike2._name);
            Console.WriteLine("{0}  {1}", mike3._age.ToString(), mike3._name);
        }
        // 0  Mike   // 0  Mike //11111  Mike
    }
  • 特殊的string 可以肯定的是:string 一定是引用类型。那它为什么是深拷贝呢? string 类型的源代码:public static readonly string Empty;//表示空字符串。此字段为只读。 </br> string 是 readonly 的,当改变 string 类型的数据值时,将重新分配了内存地址。

  • 总结

  1. 浅拷贝:是指将对象中的所有字段逐字复杂到一个新对象。 对值类型字段只是简单的拷贝一个副本到目标对象,改变目标对象中值类型字段的值不会反映到原始对象中,因为拷贝的是副本; 对引用型字段则是指拷贝他的一个引用到目标对象。改变目标对象中引用类型字段的值它将反映到原始对象中,因为拷贝的是指向堆是上的一个地址;

  2. 深拷贝:深拷贝与浅拷贝不同的是对于引用字段的处理,深拷贝将会在新对象中创建一个新的对象和原始对象中对应字段相同(内容相同)的字段,也就是说这个引用和原始对象的引用是不同, 我们改变新对象中这个字段的时候是不会影响到原始对象中对应字段的内容。

  3. 参考文章

泛型 | 匿名方法 | 委托

匿名方法

匿名类型上的Equals()的实现比较对象的状态,如果一个对象的每个属性值都与另一个对象的对应属性值相同,结果就是true。但使用==运算符不会得到这样的结果。如前几章所述,==运算符比较对象引用。 参考:C#入门经典14章

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 匿名实例
var curries = new[] {
                new { MainIngredient = "Lamb", Style = "Dhansak", Spiciness = 5  },
                new { MainIngredient = "Lamb", Style = "Dhansak", Spiciness = 5 },
                new { MainIngredient = "Chicken", Style = "Dhansak", Spiciness = 5 } 
            };
            Console.WriteLine(curries[0].GetHashCode());  // -1544265382
            Console.WriteLine(curries[1].GetHashCode());  // -1544265382
            Console.WriteLine(curries[2].GetHashCode());  // 810960096
            Console.WriteLine(curries[0].Equals(curries[1]));  // true
            Console.WriteLine(curries[0].Equals(curries[2]));  // false
            Console.WriteLine(curries[0] == curries[1]);  // false
            Console.WriteLine(curries[0] == curries[2]);  // false

泛型

  • 它有助于您最大限度地重用代码、保护类型的安全以及提高性能。

  • 可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。

  • 可以对泛型类进行约束以访问特定数据类型的方法。

  • 泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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();
        }
    }

委托

委托(delegate):委托是一种数据类型,像类一样,可以声明委托类型的变量,它可以将方法当做另一个方法来进行传递。 委托(delegate)至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型。

  • 不能直接在类中定义,而是在给委托变量赋值的时候,需要赋值一个方法,此时可以“现做现卖”,定义一个匿名方法传给委托。这个匿名方法我们要熟悉他的语法,后面进化为lambda表达式。

  • 泛型委托:如果我们有三个方法,这三个方法的参数的数据类型都不同。这时我们就要定义三个委托,泛型委托解决了这一问题。 编译器自带了一个泛型的委托Action,它有16个重载,用起来非常的方便。

  • 如果我的方法是有返回值的怎么办,这时候不要慌,我都说了微软是爱程序猿的,Func解决了这个问题,和Action的使用方式一样它只是带返回值的。

  • 代码参考-csdn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// 无返回值委托
   class Program
    {
        static void Main(string[] args)
        {
            MyDelegate md = new MyDelegate(M1);

            //调用md委托的时候就相当于调用的M1方法
            md();
            Console.ReadLine();
        }
        static void M1()
        {
            Console.WriteLine("我是一个没有返回值没有参数的方法");
        }
    }
    //定义了一个名字叫MyDelegate的类型,并且这个类型是个委托,而且只能存储没有返回值,没有参数的方法
    public delegate void MyDelegate();

//委托结合匿名方法
class Program
    {
        static void Main(string[] args)
        {
            Mydelegate md = delegate ()
              {
                  Console.WriteLine("我是一个现做现卖的匿名方法");
              };
            md();
            Console.Read();
        }
    }
    public delegate void Mydelegate();

//委托结合Lamdba表达式
class Program
    {
        static void Main(string[] args)
        {
            //无参数用小括号代表
            MyDelegate md = () => { Console.WriteLine("lambda表达式!"); };

            //有参数直接传参数,不用传递数据类型,委托已经限定数据类型
            MyDelegate1 md1 = m => { Console.WriteLine(m); };
            md();
            md1("大家好");
            Console.ReadLine();
        } 
    }
    //有参数的委托
    delegate void MyDelegate1(string msg);
    
    //无参数的委托
    delegate void MyDelegate();

//委托结合泛型
class Program
    {
        static void Main(string[] args)
        {            
            //泛型委托
            MyGenericDelegate<string> md = M1;
            md("我是参数为string类型的方法");
            MyGenericDelegate< int > md1 = M1;
            md1(250);
            Console.Read();
        }
        static void M1(int j)
        {
            Console.WriteLine(j);
        }
        static void M1(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    //泛型委托
    public delegate void MyGenericDelegate<T>(T args);

//action特性 = 委托+泛型
 class Program
    {
        static void Main(string[] args)
        {
            
            Action action1 = new Action(M1);
            action1();
            //Action action1 = M1;
            //action1();

            Action<string> al = M1;
            al("我是参数为string类型的方法");

            Action<int> al1 = M1;
            al1(250);
      
        }
        static void M1()
        {
            Console.WriteLine("我是一个无参数的方法");
        }
        static void M1(int j)
        {
            Console.WriteLine(j);
        }
        static void M1(string msg)
        {
            Console.WriteLine(msg);
        }
    }

//Func特性 带返回值的委托
class Program
    {
        static void Main(string[] args)
        {
             //第四个int表示返回值的数据类型
            Func<int, int, int, int> fun =(M1);
            int result= fun(1, 2, 4);
            Console.WriteLine(result);
            Console.Read();
        }
        static int M1(int n1, int n2, int n3)
        {
            return n1 + n2 + n3;
        }
    }


序列化

反射

web编程