目录
一、值传递和引用传递
1、概念
2、主要区别
3、用法
二、字符串
1、字符串的不可变性可以将字符串看作只读字符数组
2、字符串提供的各种方法
三、继承
1、 概念
2、格式
3、示例
4、子类继承于父类什么?
5、继承的特性
6、查看类图
四、new关键字
1、概念
2、使用 new 关键字隐藏父类成员
3、如何访问隐藏的成员
4、 隐式和显式成员隐藏
值类型和引用类型是两种基本的数据类型,它们在内存管理、存储方式以及行为上有明显的区别。以下是它们的定义、主要区别、用法示例以及相关概念的详细说明。
值类型(Value Types):
int
、float
、char
、bool
,以及结构体(struct
)和枚举(enum
)。引用类型(Reference Types):
class
)、接口(interface
)、数组(array
)和字符串(string
)。特性 | 值类型 | 引用类型 |
---|---|---|
存储方式 | 存储在栈中 | 存储在堆中 |
赋值方式 | 赋值时复制实际值 | 赋值时复制引用(地址) |
默认值 | 有默认值(如 0 、false 、'\0' 等) |
默认值为 null |
继承支持 | 不支持继承 | 支持继承 |
适用场景 | 适用于小的数据量和简单类型(如基本类型、结构体等) | 适用于复杂的数据结构(如对象、数组等) |
值类型示例
using System;
public class ValueTypeExample
{
public static void Main()
{
int a = 10; // 值类型
int b = a; // 复制 a 的值
b = 20; // 修改 b 的值
Console.WriteLine("a: " + a); // 输出: a: 10
Console.WriteLine("b: " + b); // 输出: b: 20
}
}
解释:
在这个示例中,int 是一个值类型。b 被赋值为 a 的值,因此当 b 的值被修改时,a 的值并没有改变。
using System;
public class ReferenceTypeExample
{
public class Person // 引用类型
{
public string Name { get; set; }
}
public static void Main()
{
Person person1 = new Person { Name = "Alice" }; // 创建对象
Person person2 = person1; // 赋值引用
person2.Name = "Bob"; // 修改 person2 的属性
Console.WriteLine("person1.Name: " + person1.Name); // 输出: person1.Name: Bob
Console.WriteLine("person2.Name: " + person2.Name); // 输出: person2.Name: Bob
}
}
解释:
在这个示例中,Person 是一个引用类型。person2 被赋值为 person1 的引用,因此它们指向同一个 Person 对象。当通过 person2 修改 Name 属性时,person1 的 Name 也随之改变。
string s = "abcdef" ;
//s[0] = 'b';//不能这样做,因为只读
//首先将字符串转换为char类型的数组
char[] chs = s.ToCharArray();
chs[0] = 'b' ;
//将字符数组转换为我们的字符串
s= new string(chs);
//既然可以将string看作char类型的制度数组,所以我可以通过下表去访问字符串中的某一个元素
Console.WriteLine(s[0]);
Console.WriteLine(s);
Console.ReadKey();
1、ToCharArray() ;将字符串转换成char数组
2、new string(char[] cha);能够将char数组转换为字符串
3、StringBuilder 的使用
1、length:获得当前字符串的长度
2、ToUpper:将字符串转换成大写
3、ToLower:将字符串转换成小写
4、str.Equals:比较字符串是否相等
5、StringComparison.OrdinalIgnoreCase:忽略被比较字符串的大小写
6、split :分割字符串,返回字符串类型的数组
7、contains:判断目标字符是否存在
8、raplace:替换目标字符
9、substring:截取字符串
10、startwith:判断这个字符串以什么开头
11、endwith:判断这个字符串以什么结尾
12、indexof:搜索目标字符的位置
13、lastindex:搜索最后一个目标字符的位置
14、trim:移除字符串两端的空白字符
15、isnullorempty:判断字符串是否为空
16、join:将字符串数组连接成一个字符串,使用逗号作为分隔符
using System;
public class StringManipulationExample
{
public static void Main()
{
// 初始化两个字符串
string str1 = "Hello, World!";
string str2 = "hello, world!";
// 1. 使用 Length 属性获取字符串长度
int lengthStr1 = str1.Length;
Console.WriteLine("Length of str1: " + lengthStr1); // 输出: Length of str1: 13
// 2. 使用 ToUpper 方法将字符串转换为大写
string upperStr1 = str1.ToUpper();
Console.WriteLine("Uppercase str1: " + upperStr1); // 输出: Uppercase str1: HELLO, WORLD!
// 3. 使用 ToLower 方法将字符串转换为小写
string lowerStr1 = str1.ToLower();
Console.WriteLine("Lowercase str1: " + lowerStr1); // 输出: Lowercase str1: hello, world!
// 4. 使用 Equals 方法比较字符串是否相等
bool areEqual = str1.Equals(str2);
Console.WriteLine("str1 equals str2 (case-sensitive): " + areEqual); // 输出: str1 equals str2 (case-sensitive): False
// 5. 使用 StringComparison.OrdinalIgnoreCase 忽略大小写比较字符串
bool areEqualIgnoreCase = str1.Equals(str2, StringComparison.OrdinalIgnoreCase);
Console.WriteLine("str1 equals str2 (case-insensitive): " + areEqualIgnoreCase); // 输出: str1 equals str2 (case-insensitive): True
}
}
using System;
public class DateSplitExample
{
public static void Main()
{
// 示例日期字符串
string dateStr = "2024-08-28";
// 使用 Split 方法分割字符串,指定分隔符为连字符 '-'
string[] dateParts = dateStr.Split('-');
// 输出分割后的结果
Console.WriteLine("Year: " + dateParts[0]); // 输出: Year: 2024
Console.WriteLine("Month: " + dateParts[1]); // 输出: Month: 08
Console.WriteLine("Day: " + dateParts[2]); // 输出: Day: 28
}
}
using System;
public class StringOperationsExample
{
public static void Main()
{
// 示例字符串
string sentence = "The quick brown fox jumps over the lazy dog.";
// 7. 使用 Contains 方法检查目标字符串是否存在
string wordToFind = "fox";
bool containsWord = sentence.Contains(wordToFind);
Console.WriteLine($"Contains '{wordToFind}': {containsWord}"); // 输出: Contains 'fox': True
// 8. 使用 Replace 方法替换目标字符串
string wordToReplace = "dog";
string replacementWord = "cat";
string newSentence = sentence.Replace(wordToReplace, replacementWord);
Console.WriteLine("After Replace: " + newSentence); // 输出: After Replace: The quick brown fox jumps over the lazy cat.
// 9. 使用 Substring 方法截取字符串
int startIndex = 4;
int length = 5;
string substring = sentence.Substring(startIndex, length);
Console.WriteLine("Substring: " + substring); // 输出: Substring: quick
}
}
using System;
public class StringMethodsExample
{
public static void Main()
{
// 示例字符串
string sentence = "The quick brown fox jumps over the lazy dog.";
// 10. 使用 StartsWith 判断字符串是否以某个子字符串开头
string startWord = "The";
bool startsWith = sentence.StartsWith(startWord);
Console.WriteLine($"Starts with '{startWord}': {startsWith}"); // 输出: Starts with 'The': True
// 11. 使用 EndsWith 判断字符串是否以某个子字符串结尾
string endWord = "dog.";
bool endsWith = sentence.EndsWith(endWord);
Console.WriteLine($"Ends with '{endWord}': {endsWith}"); // 输出: Ends with 'dog.': True
// 12. 使用 IndexOf 查找目标字符的位置
char targetChar = 'f';
int indexOfChar = sentence.IndexOf(targetChar);
Console.WriteLine($"Index of '{targetChar}': {indexOfChar}"); // 输出: Index of 'f': 16
// 13. 使用 LastIndexOf 查找最后一个目标字符的位置
int lastIndexOfChar = sentence.LastIndexOf(targetChar);
Console.WriteLine($"Last index of '{targetChar}': {lastIndexOfChar}"); // 输出: Last index of 'f': 16
}
}
using System;
using System.Linq;
public class StringMethodsDemo
{
public static void Main()
{
// 14. Trim: 移除字符串两端的空白字符
string originalString = " Hello, World! ";
string trimmedString = originalString.Trim();
Console.WriteLine("Original: '" + originalString + "'"); // 输出带空格的原始字符串
Console.WriteLine("Trimmed: '" + trimmedString + "'"); // 输出移除了空格的字符串
// 15. IsNullOrEmpty: 判断字符串是否为空或为null
string emptyString = "";
bool isEmpty1 = string.IsNullOrEmpty(emptyString);
Console.WriteLine("Is '" + emptyString + "' null or empty? " + isEmpty1); // 输出: True
string nullString = null;
bool isEmpty2 = string.IsNullOrEmpty(nullString);
Console.WriteLine("Is nullString null or empty? " + isEmpty2); // 输出: True
// 16. Join: 将字符串数组连接成一个字符串,使用逗号作为分隔符
string[] words = { "apple", "banana", "cherry" };
string joinedString = string.Join(", ", words);
Console.WriteLine("Joined string: " + joinedString); // 输出: apple, banana, cherry
}
}
解决代码冗余,将重复成员封装成一个类,作为一些类的父类
创建一个新类(子类或派生类),这个新类继承自一个已有的类(基类或父类)。继承允许子类自动获取父类的属性和方法,从而实现代码的重用和扩展。
//父类
public class BaseClass
{
// 字段
public int Field;
// 属性
public string Property { get; set; }
// 方法
public void Method()
{
// 方法实现
}
// 带有 virtual 修饰符的方法,允许在派生类中重写
public virtual void VirtualMethod()
{
// 方法实现
}
}
//子类
public class DerivedClass : BaseClass
{
// 新增字段
public int AdditionalField;
// 新增属性
public string AdditionalProperty { get; set; }
// 新增方法
public void NewMethod()
{
// 方法实现
}
// 重写基类的虚方法
public override void VirtualMethod()
{
// 重写方法实现
// 可以调用基类的方法
base.VirtualMethod();
}
}
class Program
{
static void Main(string[] args)
{
// 创建派生类的实例
DerivedClass derived = new DerivedClass();
// 访问基类成员
derived.Field = 10;
derived.Property = "Base Property";
derived.Method();
// 访问派生类成员
derived.AdditionalField = 20;
derived.AdditionalProperty = "Derived Property";
derived.NewMethod();
// 调用重写的方法
derived.VirtualMethod();
}
}
属性:子类继承父类的所有公共(public
)和保护(protected
)属性。属性是类中用于存储数据的成员变量。子类可以访问和修改这些继承的属性。
方法:子类继承父类的所有公共和保护方法。这些方法可以直接在子类中使用,或者在子类中进行重写(如果父类的方法是 virtual
或 abstract
)。
事件:子类继承父类定义的公共和保护事件。事件通常用于通知订阅者某些操作的发生。
.索引器:子类继承父类定义的索引器。索引器允许类的实例像数组一样使用索引进行访问
构造函数:构造函数不会被继承。子类必须定义自己的构造函数,并可以通过 base
关键字调用父类的构造函数以初始化父类的成员
访问修饰符:子类可以访问父类的公共(public
)和保护(protected
)成员,但不能访问父类的私有(private
)成员。私有成员仅在定义它的类内部可见。
内部类和静态成员:如果父类定义了内部类或静态成员,子类也可以访问这些静态成员,但内部类的访问权限取决于其定义的位置和访问修饰符。
在 Visual Studio 2022 中,点击菜单栏上的 “视图”(View)。
Ctrl+Shift+C
打开类视图。使用 new
关键字可以在子类中隐藏父类的成员。这种隐藏机制允许你在子类中重新定义与父类中同名的成员(属性、方法、字段等),而不是继承和重用父类的成员。需要注意的是,使用 new
关键字隐藏成员不会改变父类成员的行为,仅仅是在子类中定义了一个新的成员。
new
关键字隐藏父类成员当你在子类中使用 new
关键字声明一个与父类中成员同名的新成员时,子类中的成员将隐藏父类中的成员。这种情况下,父类中的成员在子类对象中将被隐藏,但仍然存在于父类的上下文中。
public class Animal
{
public void Speak()
{
Console.WriteLine("Animal speaks");
}
}
public class Dog : Animal
{
// 使用 new 关键字隐藏父类中的 Speak 方法
public new void Speak()
{
Console.WriteLine("Dog barks");
}
}
当你通过父类的引用访问子类对象时,调用的是父类中定义的成员,而不是子类中隐藏的成员。
public class Program
{
public static void Main()
{
Dog myDog = new Dog();
// 使用 Dog 类型调用 Speak 方法
myDog.Speak(); // 输出: Dog barks
// 使用 Animal 类型调用 Speak 方法
Animal myAnimal = myDog;
myAnimal.Speak(); // 输出: Animal speaks
}
}
隐式隐藏:当你仅使用 new
关键字时,编译器不会产生警告。子类中的成员会隐藏父类中的同名成员,但这可能导致代码的可读性和维护性问题,因为开发者可能不清楚隐藏的行为。
显式隐藏:你可以使用 new
关键字显式地隐藏父类成员,并建议开发者注意这一点。显式隐藏可以提高代码的明确性。