前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >.NET 面试准备:通过代码演练深入理解高级 C# 知识

.NET 面试准备:通过代码演练深入理解高级 C# 知识

作者头像
郑子铭
发布2025-03-10 20:18:20
发布2025-03-10 20:18:20
2900
代码可运行
举报
运行总次数:0
代码可运行

在.NET面试中,你很可能会遇到各种各样的C#问题,这些问题构成了.NET开发的核心内容。这些问题通常涵盖设计模式、语言特性、语言集成查询(LINQ)、委托等多个方面。在本文中,我们将逐一梳理这些问题,提供详细的解释以及经过改写的示例来帮助理解。无论你是一位有抱负的开发人员,还是经验丰富的C#专业人士,本指南都旨在成为一份全面的参考资料,帮助你为应对各种具有挑战性的C#面试做好准备。

解释以下C#代码片段中构造函数的行为并输出打印结果?

代码语言:javascript
代码运行次数:0
复制
public classVehicle
{
    publicVehicle(int yearManufactured)
    {
        Console.WriteLine($"The vehicle was manufactured in {yearManufactured}");
    }
}

publicclassCar:Vehicle
{
    publicCar(string model,int yearManufactured):base(yearManufactured)
    {
        Console.WriteLine($"The car model is {model}");
    }

    staticCar()
    {
        Console.WriteLine("Initializing the Car class...");
    }
}

classProgram
{
    staticvoidMain(string[] args)
    {
        var car1 =newCar("Sedan",);
        var car2 =newCar("SUV",);
    }
}

答案:

构造函数的顺序及执行行为:

当代码执行时,操作的顺序由继承结构以及静态构造函数的存在与否来决定:

  • 静态构造函数:Car类中的静态构造函数在首次实例化Car对象时执行一次。它会输出“Initializing the Car class...”,并且即便后续创建更多的Car对象,它也不会再次运行。
  • 基类构造函数:对于每个创建的Car对象,在执行Car构造函数中的任何内容之前,都会先调用基类Vehicle的构造函数。这确保了对象中属于Vehicle的部分能首先被正确初始化。
  • 派生类构造函数:在基类构造函数运行之后,派生类Car通过处理其自身的成员(比如打印汽车型号)来完成初始化过程。

输出结果如下:

代码语言:javascript
代码运行次数:0
复制
Initializing the Car class...
The vehicle was manufactured in 2015
The car model is Sedan
The vehicle was manufactured in 2020
The car model is SUV

2. 请根据以下代码片段以及关于继承的不同问题和答案进行分析。

代码语言:javascript
代码运行次数:0
复制
using System;

// ** 1. 一个类能否同时从一个普通类和一个抽象类继承? **
// 解释:这是不正确的,因为C#不支持多重继承。

publicclassVehicle
{
    publicvoidStart()=> Console.WriteLine("Vehicle started");
}

publicabstractclassFlyingMachine
{
    publicabstractvoidFly();
}

// 取消下面这行代码的注释将会导致编译时错误。
// public class Airplane : Vehicle, FlyingMachine // C#中不允许多重继承
// {
//     public override void Fly() => Console.WriteLine("Airplane flying");
// }

publicclassCar:Vehicle// 正确:单继承
{
    publicvoidDrive()=> Console.WriteLine("Car is driving");
}

// ** 2. 一个接口能否从一个抽象类继承? **
// 解释:这是不正确的,因为接口只能从其他接口继承。

publicabstractclassMachine
{
    publicabstractvoidOperate();
}

publicinterfaceIFlyable
{
    voidFly();
}

// 取消下面这行代码的注释将会导致编译时错误。
// public interface IFlyableMachine : Machine // 接口不能从抽象类继承
// {
// }

publicinterfaceIFlyableMachine:IFlyable// 正确:接口可以从其他接口继承
{
}

// ** 3. 一个抽象类能否从另一个抽象类继承? **
// 解释:这是正确的,因为抽象类可以从其他抽象类继承。

publicabstractclassAnimal
{
    publicabstractvoidMakeSound();
}

publicabstractclassMammal:Animal
{
    publicabstractvoidWalk();
}

publicclassDog:Mammal// 实现抽象成员的具体类
{
    publicoverridevoidMakeSound()=> Console.WriteLine("Dog barking");
    publicoverridevoidWalk()=> Console.WriteLine("Dog walking");
}

// ** 4. 一个类能否从两个或更多的基类继承? **
// 解释:这是不正确的,因为C#不支持类的多重继承。

publicclassEngine
{
    publicvoidRun()=> Console.WriteLine("Engine running");
}

publicclassTransmission
{
    publicvoidShift()=> Console.WriteLine("Shifting gears");
}

// 取消下面这行代码的注释将会导致编译时错误。
// public class SportsCar : Engine, Transmission // C#中不允许多重继承
// {
// }

publicclassSportsCar:Engine// 正确:从一个基类进行单继承
{
    publicvoidDriveFast()=> Console.WriteLine("SportsCar driving fast");
}

// ** 5. 一个类能否从两个或更多的抽象类继承? **
// 解释:这是不正确的,因为C#不支持抽象类的多重继承。

publicabstractclassAbstractClass1
{
    publicabstractvoidMethod1();
}

publicabstractclassAbstractClass2
{
    publicabstractvoidMethod2();
}

// 取消下面这行代码的注释将会导致编译时错误。
// public class CombinedClass : AbstractClass1, AbstractClass2 // C#中不允许多重抽象继承
// {
// }

publicclassCombinedClass:AbstractClass1// 正确:从一个抽象类继承
{
    publicoverridevoidMethod1()=> Console.WriteLine("Implementing Method1");
}

// ** 6. 一个类能否在从一个基类继承的同时实现多个接口? **
// 解释:这是正确的,因为C#支持单继承以及实现多个接口。

publicinterfaceISwimmable
{
    voidSwim();
}

publicclassAmphibiousVehicle:Vehicle, IFlyable, ISwimmable// 正确:从Vehicle进行单继承并实现多个接口
{
    publicvoidFly()=> Console.WriteLine("Amphibious vehicle flying");
    publicvoidSwim()=> Console.WriteLine("Amphibious vehicle swimming");
}

从上述代码片段及解释中,我们可以得出以下结论:

  • C#支持类和抽象类的单继承,这意味着一个类一次只能从一个类或抽象类继承。
  • C#中的接口不能从抽象类继承,但可以从其他接口继承。
  • C#中的类在从一个基类继承的同时可以实现多个接口,这使得设计具有灵活性和可扩展性。
  • C#中不允许类和抽象类进行多重继承,但接口允许一定程度上类似多重继承的行为。

3. “方法隐藏(method hiding)和方法重写(method overriding)之间有什么区别?以下代码的输出结果是什么?”

代码语言:javascript
代码运行次数:0
复制
public classVehicle
{
    publicstringGetInfo()=>"Generic Vehicle";
}

publicclassCar:Vehicle
{
    publicnewstringGetInfo()=>"Car";
}

classProgram
{
    staticvoidMain(string[] args)
    {
        Vehicle myVehicle =newCar();
        Console.WriteLine(myVehicle.GetInfo());
    }
}

答案:

方法隐藏(使用new关键字):派生类定义了一个新方法,该方法隐藏了基类的方法。当通过基类引用访问时,会调用基类中的方法。

方法重写(使用override关键字):派生类替换了基类的方法。即便使用基类引用,调用的也是派生类中被重写的方法。

输出结果:

代码语言:javascript
代码运行次数:0
复制
// 输出:"Generic Vehicle"

由于方法隐藏,这里调用的是基类的方法。

4. 如何使用LINQ执行左外连接?

答案:

在LINQ中,通过使用DefaultIfEmpty()可以实现左外连接,以确保左表(或集合)中的所有记录都被包含进来,即便在右表中没有匹配项也不例外。

代码语言:javascript
代码运行次数:0
复制
var employees =newList<Employee>
{
    newEmployee{ Id =, Name ="Alice"},
    newEmployee{ Id =, Name ="Bob"}
};

var departments =newList<Department>
{
    newDepartment{ EmployeeId =, DeptName ="HR"}
};

var result =from emp in employees
             join dept in departments on emp.Id equals dept.EmployeeId 
             into empDept
             from d in empDept.DefaultIfEmpty()
             selectnew{ emp.Name, Dept = d?.DeptName??"No Department"};

这个查询确保了所有员工(包括那些没有所属部门的员工)都会被包含在结果中。

5. 在C#中,能否向匿名类型的列表添加新项?为什么可以或不可以?

答案:匿名类型是在编译时创建的,一旦定义就无法修改。

代码语言:javascript
代码运行次数:0
复制
var items =newList<dynamic>
{
    new{ Id =, Name ="Item1"},
    new{ Id =, Name ="Item2"}
};
// 以下代码会导致编译时错误:
items.Add(new{ Id =, Name ="Item3"});

匿名类型一旦创建就是不可变的,并且你无法在定义它们的方法之外添加相同匿名类型的新项。

6. 如何使用lambda表达式优化以下代码?

代码语言:javascript
代码运行次数:0
复制
public classElevator
{
    publicdelegatevoidGoToFloorDelegate(int floor);
    publicGoToFloorDelegate GoToFloor {get;set;}

    publicvoidGoToReception()
    {
        GoToFloor?.Invoke();
        Console.WriteLine("I am at the reception now...");
    }
}

publicstaticvoidGoFast(int floor)
{
    Console.WriteLine($"Going fast to floor {floor}");
}

答案:可以不使用自定义委托,而是利用lambda表达式以及内置的Action委托(它可以接收参数并且无返回值)来简化代码:

代码语言:javascript
代码运行次数:0
复制
public classElevator
{
    publicAction<int> GoToFloor {get;set;}

    publicvoidGoToReception()
    {
        GoToFloor?.Invoke();
        Console.WriteLine("I am at the reception now...");
    }
}

使用lambda表达式可以简化方法调用:

代码语言:javascript
代码运行次数:0
复制
elevator.GoToFloor = floor => Console.WriteLine($"Moving fast to floor {floor}");

7. 在C#中,何时应该使用Action,何时应该使用Func

答案:

Action用于那些不返回值但最多可以接收16个参数的委托。

代码语言:javascript
代码运行次数:0
复制
Action<int> printNumber = n => Console.WriteLine(n);

Func用于那些会返回值的委托。最后一个类型参数定义了返回类型。

代码语言:javascript
代码运行次数:0
复制
Func<int, string> numberToString = n => n.ToString();

8. 根据以下代码示例填充泛型代码?

代码语言:javascript
代码运行次数:0
复制
public interfaceIVehicle
{
    int NumberOfWheels {get;set;}
}
// 在此处使用泛型实现代码
publicclassFactory
{
    
}

publicclassCar:IVehicle
{
    publicint NumberOfWheels {get;set;}
}

publicclassMotorcycle:IVehicle
{
    publicint NumberOfWheels {get;set;}
}

// 演示
classProgram
{
    staticvoidMain()
    {
        // 创建一个有4个轮子的汽车
        Car car = Factory.BuildFrame<Car>();
        Console.WriteLine($"Car with {car.NumberOfWheels} wheels.");

        // 创建一个有2个轮子的摩托车
        Motorcycle motorcycle = Factory.BuildFrame<Motorcycle>();
        Console.WriteLine($"Motorcycle with {motorcycle.NumberOfWheels} wheels.");
    }
}

答案:

BuildFrame方法旨在创建遵循IVehicle接口的任何类型的车辆(比如汽车或摩托车)。通过使用带有约束的泛型,我们确保BuildFrame只能处理那些实现了IVehicle接口并且有无参构造函数(意味着可以使用new T()来创建)的类型。在BuildFrame方法内部,我们创建类型T的一个实例,设置它的NumberOfWheels属性,然后返回该实例。这种设计使得BuildFrame能够创建并设置任何遵循IVehicle接口的车辆类型,使其对于各种车辆类来说既灵活又可复用。

代码语言:javascript
代码运行次数:0
复制
public static T BuildFrame<T>(int wheelsNumber) where T : IVehicle, new()
{
    T vehicle = new T();         // 使用new()约束创建T的一个新实例
    vehicle.NumberOfWheels = wheelsNumber;  // 设置NumberOfWheels属性
    return vehicle;
}
代码语言:javascript
代码运行次数:0
复制
点击下方卡片关注DotNet NB
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 解释以下C#代码片段中构造函数的行为并输出打印结果?
  • 2. 请根据以下代码片段以及关于继承的不同问题和答案进行分析。
  • 3. “方法隐藏(method hiding)和方法重写(method overriding)之间有什么区别?以下代码的输出结果是什么?”
  • 4. 如何使用LINQ执行左外连接?
  • 5. 在C#中,能否向匿名类型的列表添加新项?为什么可以或不可以?
  • 6. 如何使用lambda表达式优化以下代码?
  • 7. 在C#中,何时应该使用Action,何时应该使用Func?
  • 8. 根据以下代码示例填充泛型代码?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档