.NET高级技术_01各种知识点

一:多项目开发

1、一个类必须声明为public才能被别的项目(程序集)访问,不写或者写internal,那么这个类就不能被别的项目访问,只有当前程序集内部可以访问。

二:索引器

没有名字,索引器的内部本质(ILSpy的IL模式下看)类型 this[参数]{get;set;}
可以是只读或者只写(在get或者set前加上private)
字符串是只读索引,因此不能对字符串中的某个字符进行从新赋值,即只能char ch = s[5];不能s[5]=‘a’。
开发中自己写的机会很少,一道面试题:C#中索引器是否只能根据数字进行索引?是否允许多个索引器参数?答案:可以进行非数字索引,可以允许多个参数进行索引。

internal class Program
{
    static void Main(string[] args)
    {
        Person p1 = new Person();
        p1[3, 5] = "hello";
        string s = p1[1, 2];

        //Dictionary<int, string> d = new Dictionary<int, string>();
        //d[3] = "";
        Console.WriteLine(s);
        Console.ReadLine();
    }
}

class Person
{
    public string this[int x, int y]
    {
        get 
        {
            return "" + x + y;
        }
        set 
        {
            Console.WriteLine("x = " + x + "; y = " + y + "; value = " + value);
        }
    }
}

三:密闭类和静态类

1、密闭类是修饰为sealed的类,sealed不能有子类。一般只有系统中的一些基本类声明为sealed。面试题:是否可以编写一个类继承自String类?
答:不能,因为string被声明为了sealed了。
在这里插入图片描述

2、静态类:声明为static的类,不能实例化,只能定义static成员。通常用作定义扩展方法。

3、C#3.0特性:扩展方法。声明静态类,增加一个静态方法,第一个参数是被扩展类型的标记为this,然后在其他类中可以直接调用,本质上还是对静态方法调用提供的一个“语法糖”,也可以用普通静态方法的方式调用,所以不能访问private和protected成员。例子:给String扩展一个IsEmail方法。自己写的机会比较少。

internal class Program
{
    static void Main(string[] args)
    {
        //string e = "abc@163.com";
        //bool b = Person.IsEmail(e);
        //bool b = e.IsEmail();

        string s1 = "abcd";
        string s2 = s1.Repeat(3); 
        Console.WriteLine(s2);
        Console.ReadLine();
    }
}

static class Person
{
    //扩展方法
    public static bool IsEmail(this string s)
    {
        return s.Contains("@");
    }

    public static string Repeat(this string s, int count)
    {
        string result = "";
        for (int i = 0; i < count; i++)
        {
            result += s;
        }
        return result;
    }

    //public static bool IsEmail(string s)
    //{
    //    return s.Contains("@");
    //}
}

四:深拷贝、浅拷贝

如果拷贝的时候共享被引用的对象就是浅拷贝,如果被引用的对象也拷贝一份出来就是深拷贝。(深拷贝就是说重新new一个对象,然后把之前的那个对象的属性值在重新赋值给这个用户)。

    internal class Program
    {
        static void Main(string[] args)
        {
            //Person p1 = new Person();
            //p1.Name = "lilei";
            //p1.Age = 10;
            //Person p2 = p1;  //让p2指向p1当前所指向的对象
            拷贝、克隆:Clone
            //Person p3 = new Person();  //拷贝了一份出来
            //p3.Name = p1.Name;
            //p3.Age = p1.Age;
            //p1.Age = 20;

            Dog dog = new Dog();
            dog.Name = "wangcai";
            Person p1 = new Person();
            p1.Name = "lilei";
            p1.Age = 10;
            p1.Dog = dog;

            Person p2 = new Person();
            p2.Name = p1.Name;
            p2.Age = p1.Age;
            p2.Dog = (Dog)p1.Dog.Clone();

            p1.Dog.Name = "erdan";

            Console.WriteLine(p2.Dog.Name);
            Console.ReadLine();
        }
    }

    class Dog
    {
        public string Name { get; set; }

        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public Dog Dog { get; set; }
    }

五:结构体

在平时的开发中很少自己去写结构体,是一种值类型的数据。对于结构,不像类那样存在继承,一个结构体不能从另一个结构或类继承。但是结构体从基类Object继承。

    internal class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person();
            p1.Name = "lilei";
            p1.Age = 10;

            Person p2 = p1;
            p1.Age = 20;
            Console.WriteLine(p2.Age);

            Dog d1 = new Dog();
            d1.Name = "lilei";
            d1.Age = 10;

            Dog d2 = d1;
            d1.Age = 20;
            Console.WriteLine(d2.Age);

            Console.ReadLine();
        }
    }

    struct Dog
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

六:值类型和引用类型

什么是“引用类型”:引用类型派生自System.Object
什么是“值类型”:值类型均隐式派生自System.ValueType(ValueType其实也是继承自Object,不过是特立独行的一个分支)
值类型有哪些:数值类型(int、long、double、float、char)、bool、结构体、枚举。(在平时写的时候看不到Int32继承自ValueType的原因是编译器帮我们进行了处理)
引用类型有哪些字符串、数组、类、接口等

区别(主要):
引用类型变量的赋值只复制对对象的引用;引用类型在堆内存(malloc);
值类型变量赋值会拷贝一个副本;值类型在栈内存;值类型一定是sealed;

string是const引用,所以复制时,会拷贝副本,效果和值类型类似。

七:CTS、CLS、CLR

1、 .Net平台下不只有C#语言,还有VB.Net、F#等语言。IL是程序最终编译的可以执行的二进制代码(托管代码),不同的语言最终都编译成标准的IL(中间语言,MSIL);这样C#可以调用VB.Net写的程序集(Assembly,dll、exe)。在.Net平台下:不同语言之间可以互联互通、互相调用。

2、不同语言中的数据类型各不相同,比如整数类型在VB.Net中是Integer、C#中是int。.Net平台规定了通用数据类型(CTS,Common Type System),各个语言编译器把自己语言的类型翻译成CTS中的类型。int是C#中的类型,Int32是CTS中的类型;int是C#的关键字,Int32不是。

面试题:
string和String的区别是什么?答:string是C#语言的中的类型,String是CTS中的类型
int和Int32的区别是什么? 答:int是C#中的类型,Int32是CTS中的数据类型

3、不同语言的语法不一样,比如定义一个类A继承自B的C#语法是class A:B{},VB.Net的语法是Class A Inherits B。.Net平台规定了通用语言规范(CLS, Common Language Specification )。

4、IL代码由公共语言运行时(CLR, Common Language Runtime )驱动运行,CLR提供了垃圾回收(GC, Garbage Collection,没有任何引用的对象可以被自动回收,分析什么时候可以被回收)、JIT(即时编译器)。

5、值类型是放在“栈内存”中,引用类型放到“堆内存”,栈内存会方法结束后自动释放,“堆内存”则需要GC来回收。

八:拆箱、装箱

值类型赋值给Object类型变量的时候,会发生装箱:包装成Object。ValueType不也是继承自Object吗(CLR内部处理);

Object类型变量赋值给值类型赋值的时候会发生拆箱,需要做显式转换。

下面几句代码有没有错,解释一下内存是怎么变化的
int i=10;
object obj = i; //装箱 内存由栈内存转为堆内存
int j = obj; //错误
long j = (long)obj; //错误 拆箱的时候要和装箱时的数据类型一样
int j = (int)obj; //拆箱 内存由堆内存转为栈内存

拆箱的时候一定要用装箱的类型

九:关于相等 Equals

查看判断两个对象是否是同一个对象要用:object.ReferenceEquals();

因为"==",默认值是比较两个对象是不是同一个对象。所以有时候两个对象的内容相等,但是比较后还是false。

Object的Equals方法也比较两个变量指向的是否同一个对象;对象如果override了Equals方法,就可以进行内容的相同比较。

默认情况下不是调用Equals方法,需要重载"=="运算符;String等这些类是重写了Equals方法。

面试题:下面的代码有几个字符串对象
String s1 = “abc”;
string s2 = s1;
string s3 = new String(new char[] {‘a’,‘b’,‘c’ });
Console.WriteLine(s1==s3); //输出true(虽然这里s1和s3不是同一个对象,但由于string重写了Equals方法,只要内容相等也返回true)
答案:两个字符串对象

namespace Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person();
            p1.Name = "lilei";
            p1.Age = 10;

            Person p2 = p1;
            Person p3 = new Person();
            p3.Name = "lilei";
            p3.Age = 10;

            Console.WriteLine(object.ReferenceEquals(p1, p2));
            Console.WriteLine(object.ReferenceEquals(p1, p3));

            Console.WriteLine(p1 == p2);
            Console.WriteLine(p1 == p3);

            //object的Equals方法的默认实现是比较两个变量是否是同一个对象。
            string s1 = "abc";
            string s2 = s1;
            string s3 = new string(new char[] { 'a', 'b', 'c' });
            Console.WriteLine(object.ReferenceEquals(s1, s2));
            Console.WriteLine(object.ReferenceEquals(s1, s3));

            Console.WriteLine(s1 == s2);
            Console.WriteLine(s1 == s3);
            Console.ReadLine();
        }
    }

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public override bool Equals(object? obj)
        {
            if (!(obj is Person))
            {
                return false;
            }
            else
            {
                Person person = (Person)obj;
                if (this.Name == person.Name && this.Age == person.Age)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        public static bool operator ==(Person left, Person right)
        {
            return left.Equals(right);
        }

        public static bool operator !=(Person left, Person right)
        {
            return !left.Equals(right);
        }
    }
}

十:字符串暂存池(缓冲池)

字符串不可变性,字符串的“暂存池”两个特性。
字符串是引用类型,程序中会存在大量的字符串对象,如果每次都创建一个字符串对象,会比较浪费内存、性能低,因此CLR做了“暂存池”(拘留池、缓冲池、暂存池),在一些情况下对于字符串对象进行了重用。

namespace Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string s1 = "lilei";
            string s2 = "lilei";
            string s3 = "li" + "lei";
            string s4 = new string(s1.ToCharArray());
            string s5 = new string(new char[] { 'l', 'i', 'l', 'e', 'i' });
            Console.WriteLine(Object.ReferenceEquals(s1, s2));
            Console.WriteLine(Object.ReferenceEquals(s1, s3));
            Console.WriteLine(Object.ReferenceEquals(s1, s4));
            Console.WriteLine(Object.ReferenceEquals(s1, s5));
            Console.WriteLine(Object.ReferenceEquals(s4, s5));
            Console.ReadLine();
        }
    }
}

面试题:上面的代码有几个字符串对象?
答案:三个字符串对象s1、s2、s3是同一个字符串对象,在内容相同的情况下只有new才能产生一个新的字符串对象。

十一:ref、out

普通参数是“值类型传递拷贝,引用类型传递引用”,但是都不能在函数内部修改外部变量的指向,这时候要用ref或者out(相当于把变量都传进去了)。

他们作用不同:
ref的作用“方法内部修改外部变量的引用”;
out的作用“方法内部给外部变量初始化,相当于一个函数多个返回值”。

1、使用ref型参数时,传入的参数必须先被初始化,方法中可以不赋值。对out而言,必须在方法中对其完成初始化,方法外部不用初始化,初始化也没用。
2、使用ref和out时,在方法的参数和执行方法时,都要加ref或out关键字。以满足匹配。
3、out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候。

应用:
1、交换两个变量的值;
2、int.TryParse():if(int.TryParse(s,out i)),返回值表示是否转换成功,out参数表示转换成功的值。
在这里插入图片描述

namespace NP
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            person.Name = "lilei";
            int i = 10;
            //Test_1(person, i);
            Test_2(ref person, ref i);
            Console.WriteLine(person.Name);
            Console.WriteLine(i);
            Console.WriteLine("=====================");
            int i1 = 1;
            int i2 = 9;
            Swap(ref i1, ref i2);
            Console.WriteLine("i1 = " + i1 + ", i2 = " + i2);
            int i3 = 1;
            Test_3(out i3);
            Console.WriteLine("i3 = " + i3);
            string s = "456";
            int i4;
            if (int.TryParse(s, out i4))
            {
                Console.WriteLine("i4 = " + i4);
            }
            else
            {
                Console.WriteLine("不是合法整数!");
            }

            Console.ReadLine();
        }

        static void Test_3(out int i)
        {
            i = 9;
        }
        static void Swap(ref int i1, ref int i2)
        {
            int temp = i1;
            i1 = i2;
            i2 = temp;
        }

        static void Test_1(Person person, int i)
        {
            person.Name = "hanmeimei";
            i = 20;
        }

        /// <summary>
        /// ref就相当于被外部的变量传进来了,在函数内部可以改变外部变量的指向 
        /// </summary>
        /// <param name="person"></param>
        /// <param name="i"></param>
        static void Test_2(ref Person person, ref int i)
        {
            person = new Person();
            person.Name = "hanmeimei";
            i = 20;
        }
    }

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值