C#学习笔记(一)

1,499 阅读9分钟

记录一下c#学习笔记吧,编译软件下载Visual Studio Installer

注释

单行注释快捷键: (ctrl + k   ctrl + l)
取消单行注释:(ctrl + k   ctrul + u)

Hello World!

using System; // 使用System命名空间
namespace ConsoleApp // 命名空间
{
    class demo // 定义类,类名为demo
    {
        static void Main(string[] args) // c#的入口函数
        {
            Console.Write("Hello World!");
            Console.WriteLine("hello world!");
        }
    }
}
Main方法是 C# 应用程序的入口点。
Write和WriteLine的区别是:Write输出不会换行,WriteLine会换行。

控制台输出

Console.Write("Hello World!");
Console.WriteLine("hello world!");

控制台输入

Console.ReadLine()
// 输入一行数据,会等到按下回车为一行输入结束,继续向下执行。 接收到的为字符串,如果想要转换为数值型的可以使用Convert.ToInt32(<要转换的变量>);
Console.ReadKey();
// 一次只读取一个字符,当用户按下任意字符才会继续向下执行。

数据类型

在c#中数据类型分为三种:
1. 值类型
2. 引用类型
3. 指针类型

值类型

类型描述范围默认值
bool布尔值True 或 FalseFalse
byte8 位无符号整数0 到 2550
char16 位 Unicode 字符U +0000 到 U +ffff'\0'
decimal128 位精确的十进制值,28-29 有效位数(-7.9 x 1028 到 7.9 x 1028) / 100 到 280.0M
double64 位双精度浮点型(+/-)5.0 x 10-324 到 (+/-)1.7 x 103080.0D
float32 位单精度浮点型-3.4 x 1038 到 + 3.4 x 10380.0F
int32 位有符号整数类型-2,147,483,648 到 2,147,483,6470
long64 位有符号整数类型-9,223,372,036,854,775,808 到 9,223,372,036,854,775,8070L
sbyte8 位有符号整数类型-128 到 1270
short16 位有符号整数类型-32,768 到 32,7670
uint32 位无符号整数类型0 到 4,294,967,2950
ulong64 位无符号整数类型0 到 18,446,744,073,709,551,6150
ushort16 位无符号整数类型0 到 65,5350
// 常用的几种数据类型
int a= 3; // 整型
float b = 3.5f; // 浮点型,数据后面需要加上f或者F
bool c = false; // 布尔类型
char d = 'a'; // 字符型(单引号为字符型)
string e = "11232eee"; // 字符串类型(双引号为字符串型)
// 可以用sizeof来查看具体类型所占字节数
Console.WriteLine("Size of int: {0}", sizeof(int));

数据类型转换

C# 是一门强类型语言,对类型要求比较严格,但是在一定的条件下也是可以相互转换的,数据类型转换分为两种一种是隐式类型转换,另一种是显示类型转换。

//隐式数据类型转换
int a = 1;
double b = a;
// 显示数据类型转换
double a = 1;
int b = (int)a;

常用的数据类型转换方法

// Parse方法
对于string类型是互相兼容的数据类型,将string类型转换为任意基本类型。
string a = "34583764586438756";
int b = int.Parse(a);
double c = double.Parse(a);
// Convert类型转换能够将任意数据类型转换为任意类型
Convert.ToInt32(<要转换的变量>); // 转换为整型(int)
Convert.等....
隐式类型转换是安全的的,属于低精度转向高精度,显示转换需要强制转换,可能会造成精度丢失。

引用类型

对象类型

对象类型是 C# 通用类型系统中所有数据类型的终极基类。Object是System.Object类的别名。所以对象类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱

object obj;
obj = 100; // 这是装箱
int x = (int)obj; // 这是拆箱

动态类型

可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。

dynamic <变量名> = value;

image.png

image.png

字符串类型

字符串类型允许给变量分配任何字符串值。字符串类型是 System.String 类的别名。它是从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。

string str = @"C:\Windows";

加上@符号,这被称为"逐字字符串",他可以将转义字符变成普通字符使用(空格,换行符等也会被计算在其中),如果需要在里面输出一个引号则需要写一对引号,如下:

string x = @"123\123""123";  // 123\123"123

指针类型

C# 为了类型安全,默认并不支持指针。但是也并不是说 C# 不支持指针,我们可以使用unsafe关键词,开启不安全代码(unsafe code)开发模式。在不安全模式下,我们可以直接操作内存,这样就可以使用指针了。在不安全模式下,CLR并不检测unsafe代码的安全,而是直接执行代码。unsafe代码的安全需要开发人员自行检测。

// unsafe 关键词的使用
// 声明整个方法作为不安全代码
using System;  
namespace ConsoleApp
{
    class Program
    {
        static unsafe void Main(string[] args)
        {

        }
    }
}
// 声明方法的一部分作为不安全代码
using System;
namespace ConsoleApp
{
    class Program
    {
        public static void Main()
        {
            unsafe
            {
                int i = 20;
                int* p = &i;
                Console.WriteLine("Address is: {0} ", (int)p);
            }
            Console.ReadKey();
        }
    }
}

C#在有限的范围内支持指针。C#的指针是一个持有另一类型内存地址的变量。在C#中,指针只能被声明为持有值类型和数组的内存地址。与引用类型不同,指针类型不被默认的垃圾收集机制所跟踪。出于同样的原因,指针不允许指向引用类型,甚至不允许指向包含引用类型的结构类型。

定义指针

实例描述
int* pp 是指向整数的指针。
double* pp 是指向双精度数的指针。
float* pp 是指向浮点数的指针。
int** pp 是指向整数的指针的指针。
int*[] pp 是指向整数的指针的一维数组。
char* pp 是指向字符的指针。
void* pp 是指向未知类型的指针。

声明多个指针的写法如下:

int* p1, p2, p3;

指针操作符

操作符说明
*取值运算符  
&取址运算符
->通过指针处理结构体中的数据(获取或赋值)
++与–指针增、减操作
fixed用户暂时固定托管代码中引用类型的位置。
Stackallc分配内存

unsafe修饰符时指针的使用

// 使用了 **unsafe** 修饰符时指针的使用
using System;  
namespace ConsoleApp  
{  
    class Program  
    {  
        static unsafe void Main(string[] args)  
        {  
            int var = 20;  
            int* p = &var;  
            Console.WriteLine("数据是: {0} ",  var);  
            Console.WriteLine(数据存放的地址是: {0}",  (int)p);  
            Console.ReadKey();  
        }  
    }  
}

image.png

使用指针检索数据值

// 使用指针检索数据值
// 不用声明整个方法作为不安全代码,只声明方法的一部分作为不安全代码。
namespace UnsafeCodeApplication
{
    class Program
    {
        public static void Main()
        {
            unsafe
            {
                int var = 20;
                int* p = &var;
                Console.WriteLine("数据是: {0} ", var);
                Console.WriteLine("数据是: {0} ", p->ToString());
                Console.WriteLine("数据存放的地址是: {0} ", (int)p);
            }
        }
    }
}

image.png

传递指针作为方法的参数

class TestPointer
{
    public unsafe void swap(int* p, int* q)
    {
        int temp = *p;
        *p = *q;
        *q = temp;
    }

    public unsafe static void Main()
    {
        TestPointer p = new TestPointer();
        int var1 = 10;
        int var2 = 20;
        int* x = &var1;
        int* y = &var2;

        Console.WriteLine("转换前: var1:{0}, var2: {1}", var1, var2);
        p.swap(x, y);

        Console.WriteLine("转换后: var1:{0}, var2: {1}", var1, var2);
        Console.ReadKey();
    }
}

image.png

固定Fixed

在垃圾收集过程中,C#垃圾收集器可以随意移动内存中的对象。C#提供了一个特殊的关键字fixed来告诉垃圾收集器不要移动一个对象。

在句子块前输入关键字fixed,将会告知CLR块内的目标不能重定位,这样CLR就不会重定位指针指向的数据存储方位。因而在C#固定指针时,运用关键字fixed将能阻挠程序运行时无效指针的发生。

class TestPointer
{
    public unsafe static void Main()
    {
        int[] list = { 10, 100, 200 };
        fixed (int* ptr = list)

            /* 显示指针中数组地址 */
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("Address of list[{0}]={1}", i, (int)(ptr + i));
                Console.WriteLine("Value of list[{0}]={1}", i, *(ptr + i));
            }
        Console.ReadKey();
    }
}

常量

常量是固定值,程序执行期间不会改变。常量可以是任何基本数据类型,比如整数常量、浮点常量、字符常量或者字符串常量,还有枚举常量。

// 定义常量
const <数据类型> <常量名称> = 值;
const int i = 10;

封装

封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。

  • public:所有对象都可以访问;
  • private:对象本身在对象内部可以访问;
  • protected:只有该类对象及其子类对象可以访问
  • internal:同一个程序集的对象可以访问;
  • protected internal:访问限于当前程序集或派生自包含类的类型。

public

所有对象都可以访问

using System;
namespace packaging
{
    class APP1
    {
        public int lenght = 10;
    }
    class APP2
    {
        public static void Main (String[] args)
        {
            var p = new APP1();
            Console.WriteLine(p.lenght);
        }
    }
}

image.png

如果将public改为privateprotected则会收到错误消息不可访问,因为它受保护级别限制。

image.png

protected

只有在通过派生类类型进行访问时,基类的受保护成员在派生类中才是可访问的。

    class Point
    {
        protected int x;
        protected int y;
    }

    class DerivedPoint : Point
    {
        static void Main()
        {
            var dpoint = new DerivedPoint();
            var p = new Point();
            // p.x = 20;
            dpoint.x = 10;
            dpoint.y = 15;
            Console.WriteLine($"x = {dpoint.x}, y = {dpoint.y}");
        }
    }

语句 p.x = 20; 生成错误,因为它是在静态方法 Main 中生成的,而不是类 Point 的实例。

image.png

internal

只有在同一程序集的文件中,内部类型或成员才可访问

// APP1.cs
using System;
namespace demo1
{
    public class APP1
    {
        internal int i = 30;
    }
}

// APP2.cs
using System;
using static demo1.APP1;
namespace demo1
{
    internal class APP2
    {
        static void Main()
        {
            var P = new demo1.APP1();
            Console.WriteLine(P.i);
        }
    }
}

image.png

private

私有访问是允许的最低访问级别。 私有成员只有在声明它们的类和结构体中才是可访问的,如以下示例所示:

    class demo
    {
        private readonly string i = "私有成员";

        public string GetData()
        {
            Console.WriteLine(i);
            return i;
        }
    }

    class read
    {
    static void Main()
        {
            var p = new demo();
            //Console.WriteLine(p.i);
            p.GetData();
        }
    }

语句 Console.WriteLine(p.i); 生成错误不可访问,因为它具有一定的保护级别 image.png

小结

先发布一下,还挺长的,明明上学的时候学过,有种死去的记忆在攻击自己的感觉,哈哈哈!只好从头学起了!

本文正在参加「金石计划」