首先要知道 byte 在 C# 的范围是 0~255。所以变量 b 和 e 是错误。
变量 c 是错误的,字符串类型没办法转成 byte 类型。
变量 a 是无法将 char 隐式转换为 byte,正确定义加一个 byte a = (byte) 'a'; char a 会转为对应ASCII码97。
比如说下面代码是没问题的
byte a=2;
char b='a';
Console.WriteLine(a+z); //输出 99
byte 的取值范围是 0~255 ;a += 5 会溢出,结果为 4 。首先将 255 转化二进制为 11111111 ,5 的二进制为 101,然后根据加法运算如下:
11111111
101
--------------
00000100 //转化为十进制就是 4
int? 为可空类型,等同于 Nullable
int 默认为0,属于值类型。
取模,用num % 2 == 0 可以判断。
位运算逻辑并,运算的逻辑是通过二进制,都是 1 时才为 1,其余都 0,然后结果是否等于 0,代码如下
bool IsPower(int num)
{
int result = num & 2;
return result == 0;
}
int Seq(int num)
{
int n = 1;
while (num / 2 != 1)
{
num = num / 2;
n += 1;
}
return n;
}
s = s + 1; 先进行右边运算,1 默认是 int,s + 1 的返回值是 int , 然后再将右边得出的结果赋值给 s ,这时是不能将 int 类型隐式转换为 short 类型的。所以要加一个强制转换 s = (short)(s + 1) 。
而 s += 1;等价于 s = (short)(s + 1) ,它做了数据类型的转型,所以编译成功。
public class Class1
{
public static int n = 0;
static Class1()
{
n++;
}
public Class1()
{
n++;
}
}
Console.WriteLine("{0}", Class1.n); //1
Class1 class1 = new Class1();
Console.WriteLine("{0}",Class1.n); //2
静态构造方法只会调用一次,而实例化几次就会运行几次。
class A
{
public static int x;
static A()
{
x = Program.y + 1;
}
}
class Program
{
public static int y = A.x + 1;
static Program() { }
static void Main(string[] args)
{
Console.WriteLine("x={0},y={1}", A.x, y);
//输出 x=1,y=2
Console.Read();
}
}
Console.WriteLine(Math.Round(11.5)); //12
Console.WriteLine(Math.Round(11.2)); //11
Console.WriteLine(Math.Round(11.7)); //12
Console.WriteLine(Math.Round(-11.5)); //-12
Console.WriteLine(Math.Round(-11.2)); //-11
Console.WriteLine(Math.Round(-11.7)); //-12
i++ 是先赋值,再自增;++i 是先自增,再赋值。
使用位运算左移(<<)比较快,但这还要加一个前提乘数必须是 2 的次方,这样才能成立。解题的过程是将 2 转化为二进制 0000 0000 0000 0010 ,然后左移 1 位 0000 0000 0000 0100 转化成十进制就是 4;左移的位数刚好是乘数的次方。8 刚好是 2 的 3 次方,所以 2 乘以 8 相当于 2<<3。
11. 什么是装箱和拆箱
装箱就是隐藏地将一个值 类型转换成引用类型,如:
int i = 1;
object o = i;
拆箱就是将引用类型转换成值类型,如:
int i = 1;
object o = i;
int n = (int)o;
分配不同
值类型存储在栈中,引⽤类型存储在堆中
释放方式不同
值类型在作用域内结时,会被系统自动释放,可心减少托管堆的压力;引用类型则靠GC,所以在性能上值类型优势比较明显。
继承不同
引用类型继承Object,且也可以被其它类继承。值类型不能作为基类,但是可以继承或者多继承接口。另外,值类型是继承ValueType,而引用类型不会继承ValueType。
传参方式不一样
值类型在传参的时候是复制内存的数据,而引用类型通过它们的引用来查找的,所以传参是共享一个内存地址。
初始化行为不同
引用类型需要通过 new 来创建对象。
其它,可以自行补充,至少回答3点吧。
运算符:创建对象实例
修饰符:隐藏掉基类⽅法
public class Class1
{
public void Add()
{
Console.WriteLine("Hello world!");
}
}
public class Class2 : Class1
{
public new void Add()
{
Console.WriteLine("Hello C#!");
}
}
约束:可以约束泛型类型,如:
public class Repository where T : class, new()
{
}
使用 Repository
引用命名空间;为命名空间或类型创建别名。
释放资源,实现了IDisposiable的类在using中创建,using结束后会⾃定调⽤该对象的Dispose⽅法, 释放资源。
const 不能用 static 修饰,只能通过类来访问;readonly 由实例对象来访问,可以用 static 来修饰。
const 只能定义为值类型和 string 类型,readonly 没有限制类型。
const 必须初始化值,在编译时会进行解析,readonly 可以在声明时和构造函数中进行初始化,不同的构造函数可以实现不同的初始值。
const 可以定义为字段和局部变量,readonly 则只能定义字段
这些的区别在于初始化对象内存分配上,str=null 还未分配内存空间,str="" 和 str=string.Empty 分配了内存空间,且初始值都是""; Empty 是 static readonly 修饰的字段。
string str = string.Empty;
string str1 = "";
string str2 = null;
Console.WriteLine(ReferenceEquals(str, str1));//True
Console.WriteLine(ReferenceEquals(str, str2));//False
Console.WriteLine(ReferenceEquals(str1, str2));//False
可以通过"即时"面板查看内存分配情况:
&str
0x0000000f6f77e4d0
*&str: 0x0000015c00001360
&str1
0x0000000f6f77e4c8
*&str1: 0x0000015c00001360
&str2
0x0000000f6f77e4c0 //开辟了栈空间,完成初始化对象
*&str2: 0x0000000000000000 //未分配堆空间,即还未有值
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。~摘自百度百科~
主要用于事件和函数回调。
强类型是在编译的时候就要确定数据类型,在执行的时候不能更改;而弱类型是在运行的时候才能确定数据类型。两者没有好坏,强类型在编译的时候确认数据类型安全性相对高一些,而且效率高。
int i;
Foo(ref i);//错误
Foo(out i);//通过
public void Foo(ref int i)
{
int n=i;//通过,因为 i 已经初始化值
}
public void Foo(out int i)
{
int n=i;//错误,已初始化不可直接使用
}
public void Foo(ref int i)
{
Console.WrilteLine(i);//通过
}
public void Foo(out int i)
{
i = 10; //这里必须赋值,不然会报未赋值
Console.WriteLine(i);
}
对于值类型,两者比较的是变量的值
int n = 5;
int m = 5;
Console.WriteLine("n == m :{0}", n == m); //True
Console.WriteLine("n.Equals(m) :{0}", n.Equals(m)); //True
对于引用类型,如果重写了 == 操作符,则比较的值,或者比较的是引用地址,看两段代码
string str = "Hello C#!";
string str1 = "Hello C#!";
Console.WriteLine("str == str1 :{0}", str == str1); //True
Console.WriteLine("str.Equals(str1) :{0}", str.Equals(str1)); //True
可以知道 string 的 == 与 Equals 返回都是 True ,通过反编译的工具找到到代码如下:
public static bool operator ==(String? a, String? b)
{
return a.Equals(b);
}
再看另外一段代码
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public Student(int id, string name)
{
Id = id;
Name = name;
}
}
Student stu = new Student(10086,"程序员");
Student stu0 = new Student(10086,"程序员");
Console.WriteLine("stu == stu0 :{0}", stu == stu0); //False
Console.WriteLine("stu.Equals(stu0) :{0}", stu.Equals(stu0)); //False
另外,两个对像值相同(x.Equals(y)==true),其HashCode也是相同的。
as 在转换的同时判断兼容性,如果无法转换返回 null ,as 转换是否成功判断的依据是结果是否为 null
is 只是做类型兼容性的判断,并不执行真正的类型转换,返回 true 或 false
效率上 as 比 is 更高,as 只做一次类型兼容检查
静态变量使用 static
修饰符进行声明,不带有 static
声明的变量为非静态变量
静态变量在类被实例化时创建,通过类进行访问;非静态变量在对象被实例化时创建,通过对象进行访问
静态函数的实现里不能使用非静态成员,包含非静态变量和非静态函数;但非静态函数可以使用静态成员
struct 是值类型;class 是引用类型
不能声明无参数构造函数。每个 struct类型已经提供了一个隐式的无参构造函数,该构造函数会对声明的字段或属性进行设置默认值。从 C# 10 开始可以声明无参数构造函数 。
不能对实例字段或属性时进行初始化。但可以在声明中初始化静态或常量字段或静态属性。从 C# 10 开始可以对实例字段或属性进行初始化。
struct 类型的构造函数必须对所有实例字段或属性进行初始化。
struct 类型不能从其他 class 或 struct 类型继承,也不能作为 class 基础类型。不过,struct 可以继承接口。
不能在 struct 类型中声明终结器(即:析构函数)。
struct 使用方式,一种通过 new 来创建;另外,也可以使用值类型那样来定义。
struct 是深度拷贝。
重写:派生类(子类)继承基类(父类),而子类与父类中的方法名、参数、返回类型相同,就称子类重写父类的方法。父类方法必须用关键字 virtual 修饰,子类必须用关键字 override 修饰。
///
/// 父类
///
public virtual void Read()
{
}
///
/// 子类重写
///
public override void Read()
{
}
重载:在同一个类中,方法名必须相同,参数必须不相同,返回类型可以不相同
public void Test(int x,int y){}
public void Test(int x,ref int y){}
public void Test(int x,int y,string a){}
构造函数不能被继承,所以不能被重写,只能被重载。
相同点:
都不能被实例化
可以被继承
可以包含方法的声明
子类必须实现未实现的方法
不同点:
接口不能包含实例字段
接口支持多重实现,抽象类只能单一继承
接口中没有实现方法体,只负责功能定义,即一个行为的规范;抽象类中的方法可以实现,也可以不实现,有抽象方法的类一定要用 abstract 修饰。
接口没有构造方法,抽象类有构造方法
抽象类⽤abstract修饰、接⼝⽤interface修饰
接口中方法默认是public,抽象类可以用 protected 修饰
接口可以继承接口,但不可以继承类,抽象类可以实现接口,而可以继承实现类,如果实现类有构造函数,抽象类必须实现。
public class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
}
public abstract class Annimal : Person
{
protected Annimal(string name) : base(name)
{
}
}
对父类成员进行重用,增加代码的可读性、灵活性。
class A
{
public A()
{
PrintFields();
}
public virtual void PrintFields(){}
}
class B : A
{
int x = 1;
int y;
public B()
{
y = -1;
}
public override void PrintFields()
{
Console.WriteLine("x={0},y={1}",x,y);
}
}
输出:x=1,y=0
这题主要考查的是实例化执行的顺序。
不考虑继承的执行顺序:
静态字段
静态构造方法
实例字段
实例构造方法
存在继承关系的执行顺序
子类的静态字段
子类的静态构造函数
子类的实例字段
父类的静态字段
父类的静态构造方法
父类的实例字段
父类的实例构造函数
子类的实例构造函数
string
有没有Length()这个方法?都没有Length()这个方法,只有Length属性。
swtich 表达式是一个整型(byte、int、long等),字符或字符串类型。
引用命名空间,也可 using 别名;也可以释放资源,实现了 IDisposiable 的类在 using 中创建,using 结束后会自动释放该对象的Disposiable();
先整理这些。
最后,祝大家学习愉快!