很久没有用写C#代码了,最近花了点时间重温了一下C#语法的一些知识点,查缺补漏!
//三目运算符:?:
//如 a>0?1:2
int a = -1;
// 通过?以及:将表达式分为3部分
var result = a > 0 ? 1 : 2;
// ?号前表示条件,得到一个bool值,如果为真,返回:号前面的结果,否则返回:号后面的结果
Console.WriteLine(result);
// 当a>0 返回1
// 当a<0 返回2
// 否则 返回0
代码示例:
a = 1;
result = a > 0 ? 1 : (a < 0 ? 2 : 0);//1
Console.WriteLine(result);
a = -1;
result = a > 0 ? 1 : (a < 0 ? 2 : 0);//2
Console.WriteLine(result);
a = 0;
result = a > 0 ? 1 : (a < 0 ? 2 : 0);//0
Console.WriteLine(result);
// 针对数据集合进行遍历
int[] values = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine(values);
for (int i = 0; i < values.Length; i++)
{
// i:0 1 2 3 4 5 6 7 8
// 0 2 4 6 8
if (i % 2 == 0)
Console.WriteLine(values[i]);
if (i % 2 > 0) continue;
Console.WriteLine(values[i]);
// break : 跳转循环,循环终止
// continue : 结当次循环(后面的代码不执行了),直接执行下次循环
}
int index = 0;
// foreach:遍历每一个元素
foreach (int i in values)
{
Console.WriteLine(i);
index++;
}
// while :首先判断条件,条件满足进入循环。可能一次都执行不了
index = 5;
while (index < 5)
{
// 死循环 index < 5一直为True
Console.WriteLine("Hello");
index++;
}
Console.WriteLine("while结束");
// do..while 选择执一遍,再判断条件,条件满足,继续执行,不满足即跳出。至少可以执一次
index = 5;
do
{
Console.WriteLine("Hello");
index++;
} while (index < 5);
你可以把访问修饰符想象成一个家庭的不同房间和钥匙。public
就像是客厅,任何人都可以进入。private
是你的私人房间,只有你自己可以进入。protected
像是家庭成员共用的房间,只有家庭成员(派生类)可以进入。internal
则是整个家(同一程序集)的公共空间,只要是住在这个家里的人都可以进入。protected internal
和private protected
则是钥匙的组合,给予更多灵活的访问控制。
public
:完全公开的。同一程序集中的任何其他代码或引用该程序集的其他程序集都可以访问该类型或成员。 某一类型的公共成员的可访问性水平由该类型本身的可访问性级别控制。(访问级别 = 0)private
:只有同一 class 或 struct 中的代码可以访问该类型或成员。(访问级别 = 5)protected
:只有同一 class 或者从该 class 派生的 class 中的代码可以访问该类型或成员。(访问级别 = 4)internal
:同一程序集中的任何代码都可以访问该类型或成员,但其他程序集中的代码不可以。(访问级别 = 3)protected internal
:该类型或成员可由对其进行声明的程序集或另一程序集中的派生 class 中的任何代码访问。(访问级别 = 2)private protected
:该类型或成员可以通过从 class 派生的类型访问,这些类型在其包含程序集中进行声明。(访问级别 = 1)假设一个类是一个“房子”的蓝图。静态方法就像是这个蓝图上标注的固定信息,比如房屋的总平方尺数——你不需要建造一个实际的房子(实例化一个对象)来获取这个信息。非静态方法则像是房子里的开关或电器,你需要进入一个实际的房子(创建一个对象)来操作它们。
静态方法
: 静态方法属于类本身,而不是类的实例。因此,无需创建类的实例就可以调用静态方法, 使用static
关键字进行定义。
非静态方法
: 非静态方法属于类的实例。需要先创建类的对象,然后才能调用这些方法。通常不需要特殊关键字,除非用于继承或重写等特殊情况。
定义方式: 使用方括号 []
。
特性: 长度固定,元素类型相同。
访问: 使用索引,从 0
开始。
示例:
int[] numbers = new int[5] {1, 2, 3, 4, 5};
常用操作: Length
获取长度, SetValue
和 GetValue
设置和获取值。
定义方式: 使用 List
类。
特性: 动态大小,元素类型相同。
访问: 使用索引,从 0
开始。
示例:
List numbers = new List {1, 2, 3, 4, 5};
常用操作: Add
, Remove
, Count
, Contains
。
定义方式: 使用 Dictionary
类。
特性: 键值对存储,键唯一。
访问: 使用键。
示例:
Dictionary age = new Dictionary
{
{"Alice", 30},
{"Bob", 40}
};
常用操作: Add
, Remove
, ContainsKey
, TryGetValue
。
定义方式: 使用 Queue
类。
特性: 先进先出(FIFO)。
访问: 不能使用索引。
示例:
Queue numbers = new Queue();
常用操作: Enqueue
, Dequeue
, Peek
, Count
。
定义方式: 使用 Stack
类。
特性: 后进先出(LIFO)。
访问: 不能使用索引。
示例:
Stack numbers = new Stack();
常用操作: Push
, Pop
, Peek
, Count
。
定义方式: 使用 HashSet
类。
特性: 元素唯一,无序。
访问: 不能使用索引。
示例:
HashSet numbers = new HashSet {1, 2, 3};
常用操作: Add
, Remove
, Contains
, Count
。
类型 | 长度是否固定 | 元素是否唯一 | 是否有序 | 可通过索引访问 | 使用场景 |
---|---|---|---|---|---|
数组 | 是 | 否 | 是 | 是 | 固定大小 |
列表 | 否 | 否 | 是 | 是 | 动态内容 |
字典 | 否 | 键是 | 否 | 否(通过键访问) | 配置设置 |
队列 | 否 | 否 | 是(FIFO) | 否 | 打印队列、等待列表 |
栈 | 否 | 否 | 是(LIFO) | 否 | 撤销操作、深度优先搜索 |
散列集 | 否 | 是 | 否 | 否 | 停止词、唯一标识符集 |
假设你要从一个魔法店里取出一些物品。
out
就像是你给店主一个空袋子,店主一定会在里面放一些东西。ref
就像是你给店主一个已经有东西的袋子,店主可以查看里面的东西,也可以添加或更改里面的东西。out
参数用于从方法返回多个值。使用 out
参数时:
out
参数赋值。public void GetData(out int x, out string y)
{
x = 10;
y = "hello";
}
// 调用
int a;
string b;
GetData(out a, out b); //此时a=10,b="hello"
ref
参数用于双向传递。即:
ref
参数的初始值。ref
参数的值,该更改将反映到外部变量。public void ModifyData(ref int x)
{
x = x * 2;
}
// 调用
int a = 5;
ModifyData(ref a); // a 现在是 10
变量
(也称为字段)是类、结构或枚举中定义的成员,用于存储数据。它们可以有访问修饰符(如 public
, private
等)。
public class MyClass
{
public int MyField; // 公有字段
private string anotherField; // 私有字段
}
属性
提供了一种封装字段的方式,允许你通过 get
和 set
访问器来控制字段的读取和写入。这有助于实现更好的数据封装和验证逻辑。
public class MyClass
{
public int MyProperty_0 { get; set; }
public int MyProperty_1 { get; }// 只有get没有set,表示这个属性是只读
public int MyProperty_2 { get; private set; } //外部不可修改
//public int MyProperty_2 { set; }// 不允许只写不读
//更灵活的写法
private int _myField; // 私有字段
public int MyProperty // 公有属性
{
get { return _myField; }
set
{
if (value >= 0) //进行更多操作
_myField = value;
}
}
}
构造函数
就像是产品(对象)出厂时的“初始化设置”过程。在这个过程中,工厂(构造函数)会根据需求(参数)来设置产品(对象)的各种特性和功能(字段和属性)。
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age) //构造函数
{
this.Name = name;
this.Age = age;
}
}
// 使用
Person person = new Person("Alice", 30);
一个类可以拥有多个构造函数
,这被称为构造函数
的重载
。
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
// 无参数的构造函数
public Person()
{
Name = "Unknown";
Age = 0;
Email = "[email protected]";
}
// 接受一个参数的构造函数
public Person(string name)
{
Name = name;
Age = 0;
Email = "[email protected]";
}
// 接受两个参数的构造函数
public Person(string name, int age)
{
Name = name;
Age = age;
Email = "[email protected]";
}
// 接受三个参数的构造函数
public Person(string name, int age, string email)
{
Name = name;
Age = age;
Email = email;
}
}
// 使用无参数构造函数
Person person1 = new Person();
// 使用一个参数的构造函数
Person person2 = new Person("Alice");
// 使用两个参数的构造函数
Person person3 = new Person("Bob", 30);
// 使用三个参数的构造函数
Person person4 = new Person("Charlie", 40, "[email protected]");
封装(Encapsulation):
封装是将数据(字段)和相关操作(方法)封装在一个类中的概念。这可以通过访问修饰符(如public
、private
、protected
)来实现,以控制数据的访问级别,防止直接访问内部数据。
这有点像手机的外壳,你可以使用屏幕、按钮等接口来与手机交互,但不能直接触及手机内部的电路。
例子:
public class Person
{
private string name;
private int age;
public string GetName()
{
return name;
}
public void SetName(string newName)
{
name = newName;
}
public int GetAge()
{
return age;
}
public void SetAge(int newAge)
{
if (newAge >= 0)
{
age = newAge;
}
}
}
//name和age字段被封装在Person类中,并通过公有方法GetName()、SetName()、GetAge()和SetAge()来访问和修改它们,防止直接访问字段。
继承(Inheritance):
public class Vehicle
{
public string Make { get; set; }
public string Model { get; set; }
public void StartEngine()
{
Console.WriteLine("Engine started.");
}
}
public class Car : Vehicle
{
public int Year { get; set; }
public void Accelerate()
{
Console.WriteLine("Car is accelerating.");
}
}
//在这个例子中,Car类继承了Vehicle类的Make、Model属性和StartEngine()方法,并且添加了自己的属性Year和方法Accelerate()。
多态(Polymorphism):
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Some generic animal sound.");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof! Woof!");
}
}
public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Meow!");
}
}
//Animal类有一个虚拟方法MakeSound(),Dog和Cat类分别重写了这个方法,使得它们可以表现出不同的声音。其中还使用virtual 和 override 关键字是一种明确的方式来声明和表达方法的重写关系,有助于代码的可读性和维护性。
interface
关键字。public
,不允许添加其他访问修饰符。示例:
1.定义接口
//假设正在开发一个绘图程序,需要支持多种图形(如圆形、矩形等)。可以定义一个IDrawable接口:
interface IDrawable
{
void Draw();
}
2.实现接口定义类
//定义不同的图形类,如Circle和Rectangle,并实现IDrawable接口:
class Circle : IDrawable
{
public void Draw()
{
Console.WriteLine("Drawing a circle.");
}
}
class Rectangle : IDrawable
{
public void Draw()
{
Console.WriteLine("Drawing a rectangle.");
}
}
3.使用
IDrawable[] drawables = new IDrawable[] { new Circle(), new Rectangle() };
foreach (var drawable in drawables)
{
drawable.Draw();
}
//Drawing a circle.
//Drawing a rectangle.
abstract
关键字。public
、protected
等)。示例:
1.定义抽象类
public abstract class Shape
{
public abstract void Draw(); // 抽象方法
public void Move() // 具体方法
{
Console.WriteLine("Moving the shape.");
}
}
2.继承抽象类
//定义Circle和Rectangle类,并继承Shape:
class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle.");
}
}
class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle.");
}
}
3.使用
Shape[] shapes = new Shape[] { new Circle(), new Rectangle() };
foreach (var shape in shapes)
{
shape.Draw();
shape.Move();
}
//Drawing a circle.
//Moving the shape.
//Drawing a rectangle.
//Moving the shape.
精彩推荐:
【C#进阶一】C#中的数组(Array)、集合(ArrayList,Queue,Stack, HashList)、List<T>、字典(Dictionary<K,T>)和双向链表LinkedList
【C#进阶八】C#中的序列化与反序列化下(二进制序列化、XML序列化及JSON序列化)
希望有所帮助,同时欢迎关注我,后面将更新更多相关内容!