1679020949
在这篇 C# 文章中,我们将了解 50 个最常见的 C# 面试问题和答案。C# 是最流行的编程语言之一,也是 .NET 开发的首选语言。因此,如果您是一名 .NET 开发人员,准备参加 .NET 面试,您将被问到有关 C# 编程的问题。以下是面向初学者和专业 C# 开发人员的前 50 个 C# 面试问题和答案。
C# 是一种计算机编程语言。Microsoft 于 2000 年开发了 C#,以提供一种现代通用编程语言,可用于开发针对各种平台(包括 Windows、Web 和 Mobile)的各种软件,仅使用一种编程语言。今天,C# 是世界上最流行的编程语言之一。数以百万计的软件开发人员使用 C# 来构建各种软件。
C# 是构建 Microsoft .NET 软件应用程序的主要语言。开发人员可以使用 C# 构建几乎所有类型的软件,包括 Windows UI 应用程序、控制台应用程序、后端服务、云 API、Web 服务、控件和库、无服务器应用程序、Web 应用程序、本机 iOS 和 Android 应用程序、AI 和机器学习软件,和区块链应用。
C# 在 Visual Studio IDE 的帮助下提供快速的应用程序开发。C# 是一种现代的、面向对象的、简单的、通用的和面向性能的编程语言。C# 是基于多种编程语言的最佳特性和用例开发的,包括 C++、Java、Pascal 和 SmallTalk。
C# 语法类似于 C++。.NET,而 C# 库类似于 Java。C# 支持现代面向对象的编程语言特性,包括抽象、封装、多态和继承。C# 是一种强类型语言。.NET 中的大多数类型都继承自 Object 类。
C# 支持类和对象的概念。类具有字段、属性、事件和方法等成员。这是一篇关于 C# 和 OOP 的详细文章。
C# 用途广泛且现代,支持现代编程需求。自诞生以来,C#语言经历了各种升级。C# 的最新版本是 C# 11。
C#语言是一种面向对象的编程语言。类是 C# 的基础。类是定义数据结构以及如何存储、管理和传输数据的模板。类具有字段、属性、方法和其他成员。
类是概念,而对象是真实的。对象是使用类实例创建的。类定义对象的类型。对象在计算机内存中存储实际值。
任何具有某些特征或可以执行某些工作的现实世界实体都称为对象。该对象也称为实例,即编程语言中实体的副本。对象是类的实例。
例如,我们需要创建一个处理汽车的程序。我们需要为汽车创建实体。让我们称它为一个类,Car。一辆汽车有四个属性,即型号、类型、颜色和尺寸。
为了在编程中表示一辆汽车,我们可以创建一个具有四个属性的 Car 类,Model、Type、Color 和 Size。这些被称为类的成员。一个类有多种类型的成员、构造函数、字段、属性、方法、委托和事件。类成员可以是私有的、受保护的或公共的。此外,由于可以在类外访问这些属性,因此它们可以是公共的。
对象是类的实例。一个类可以根据需要拥有任意数量的实例。例如,本田思域是汽车的一个实例。在实际编程中,本田思域是一个对象。因此,本田思域是 Car 类的一个实例。Honda Civic 的 Model、Type、Color 和 Size 属性分别为 Civic、Honda、Red 和 4。BMW 330、Toyota Carolla、Ford 350、Honda CR4、Honda Accord 和 Honda Pilot 是 Car 对象的更多示例。
托管代码
“托管代码是使用 .NET 框架及其支持的编程语言(如 C# 或 VB.NET)开发的代码。托管代码由Common Language Runtime(CLR或Runtime)直接执行,Runtime管理其生命周期,包括对象创建、内存分配和对象处置。任何用 .NET Framework 编写的语言都是托管代码”。
非托管代码
在 .NET 框架之外开发的代码称为非托管代码。
“不在 CLR 控制下运行的应用程序被称为非托管应用程序。例如,C 或 C++ 或 Visual Basic 等语言是非托管的。
程序员直接管理非托管代码的对象创建、执行和处置。因此,如果程序员编写了糟糕的代码,可能会导致内存泄漏和不必要的资源分配。”
.NET Framework 提供了一种在托管代码中使用非托管代码的机制,反之亦然。该过程是在包装类的帮助下完成的。
装箱和拆箱都用于类型转换。
从值类型转换为引用类型称为装箱。装箱是一种隐式转换。下面是 C# 中的装箱示例。
// Boxing
int anum = 123;
Object obj = anum;
Console.WriteLine(anum);
Console.WriteLine(obj);
从引用类型转换为值类型称为拆箱。下面是在 C# 中取消装箱的示例。
// Unboxing
Object obj2 = 123;
int anum2 = (int)obj;
Console.WriteLine(anum2);
Console.WriteLine(obj);
Class 和 struct 都是用户定义的数据类型,但有一些主要区别:
结构体
班级
阅读以下文章以了解有关 C# 中的结构与类、结构和类差异的更多信息。
以下是 C# 中接口和抽象类之间的一些常见区别。
要了解有关抽象类和接口之间区别的更多信息,请访问 抽象类与接口。
枚举是一种具有一组相关命名常量的值类型,通常称为枚举器列表。enum 关键字用于声明枚举。它是用户定义的原始数据类型。
枚举类型可以是整数(float、int、byte、double 等)。但是如果你在 int 旁边使用它,它必须被强制转换。
枚举用于在 .NET 框架中创建数字常量。枚举的所有成员都是枚举类型。因此,每个枚举类型都必须有一个数值。
枚举元素的底层默认类型是 int。默认情况下,第一个枚举器的值为 0,每个后续枚举器的值增加 1。
enum Dow {Sat, Sun, Mon, Tue, Wed, Thu, Fri};
关于枚举的一些要点,
使用 break 语句,您可以“跳出循环”,而使用 continue 语句,您可以“跳过一次迭代”并恢复循环执行。
例如,中断语句
using System;
using System.Collections;
using System.Linq;
using System.Text;
namespace break_example {
Class brk_stmt {
public static void main(String[] args) {
for (int i = 0; i <= 5; i++) {
if (i == 4) {
break;
}
Console.WriteLine("The number is " + i);
Console.ReadLine();
}
}
}
}
输出
数量为0;
数量为1;
数量为2;
数量是3;
例如,继续声明
using System;
using System.Collections;
using System.Linq;
using System.Text;
namespace continue_example {
Class cntnu_stmt {
public static void main(String[] {
for (int i = 0; i <= 5; i++) {
if (i == 4) {
continue;
}
Console.WriteLine("The number is "+ i);
Console.ReadLine();
}
}
}
}
输出
数量为1;
数量为2;
数量是3;
数量为5;
Const 只不过是“常量”,一个变量,其值在编译时是常量。因此,必须为其分配一个值。默认情况下,const 是静态的,我们不能在整个程序中更改 const 变量的值。
Readonly 是我们可以在运行时更改其值或在运行时分配它的关键字,但只能通过非静态构造函数。
例子
我们有一个测试类,其中有两个变量,一个是只读的,另一个是常量。
class Test {
readonly int read = 10;
const int cons = 10;
public Test() {
read = 100;
cons = 100;
}
public void Check() {
Console.WriteLine("Read only : {0}", read);
Console.WriteLine("const : {0}", cons);
}
}
在这里,我试图在构造函数中更改两个变量的值,但是当我尝试更改常量时,在我必须在运行时调用的块中更改它们的值会出错。
最后,从类中删除该行代码并调用此 Check() 函数,如以下代码片段所示:
class Program {
static void Main(string[] args) {
Test obj = new Test();
obj.Check();
Console.ReadLine();
}
}
class Test {
readonly int read = 10;
const int cons = 10;
public Test() {
read = 100;
}
public void Check() {
Console.WriteLine("Read only : {0}", read);
Console.WriteLine("const : {0}", cons);
}
}
输出
ref 关键字通过引用传递参数。因此,当控件返回到调用方法时,在方法中对此参数所做的任何更改都将反映在该变量中。
out 关键字通过引用传递参数。这与 ref 关键字非常相似。
要了解有关 ref 和 out 关键字的更多信息,请阅读以下文章: Ref Vs。C# 中的输出关键字
我们不能在静态方法中使用“this”,因为关键字“this”返回对包含它的类的当前实例的引用。静态方法(或任何静态成员)不属于特定实例。它们在不创建类实例的情况下存在,并且使用类名而不是实例调用,因此我们不能在静态方法的主体中使用 this 关键字。但是,在扩展方法的情况下,我们可以使用方法的参数。
让我们看一下“this”关键字。
C#中的“this”关键字是一种特殊类型的引用变量,隐式定义在每个构造函数和非静态方法中,作为定义它的类型类的第一个参数。
在 C# 中,属性是类的成员,该类提供读取、写入或计算私有字段值的方法。它公开了一个公共接口来访问和修改存储在类中的数据,同时允许类保持对数据访问和操作方式的控制。
使用 get 和 set 访问器声明属性,它们定义获取或设置属性值的行为。get 访问器检索属性的值,而 set 访问器设置属性的值。属性可以有一个或两个访问器,具体取决于它是只读的、只写的还是读写的。
例如,考虑一个带有私有字段名称的 Person 类。然后,可以像这样创建一个属性 Name 来提供对该字段的访问。
class Person
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
此属性允许从类外部访问名称字段,但只能通过属性方法。这提供了对数据访问和修改方式的封装和控制级别。
C# 中的属性是面向对象编程的重要组成部分,广泛用于应用程序中,以提供一种干净、安全的方式来访问和修改类数据。
在此处了解更多信息:C# 中的属性
在 C# 中,扩展方法是一种静态方法,用于扩展现有类型的功能,而无需修改原始类型或创建新的派生类型。扩展方法允许开发人员向现有类型添加方法,例如类、结构、接口、枚举等,这些类型最初未定义。
扩展方法在静态类中声明,并被定义为具有特殊第一个参数(称为“this”参数)的静态方法。“this”参数指定被扩展的类型,并允许调用扩展方法,就好像它是该类型的实例方法一样。
例如,考虑以下扩展方法,它通过提供一种方法将字符串的第一个字母大写来扩展字符串类型:
public static class StringExtensions
{
public static string CapitalizeFirstLetter(this string str)
{
if (string.IsNullOrEmpty(str))
return str;
return char.ToUpper(str[0]) + str.Substring(1);
}
}
使用此扩展方法,可以在任何字符串对象上调用 CapitalizeFirstLetter 方法,如下所示:
string s = "hello world";
string capitalized = s.CapitalizeFirstLetter(); // "Hello world"
请注意,CapitalizeFirstLetter 方法未在字符串类中定义,而是由 StringExtensions 类提供的扩展方法。
扩展方法是 C# 中的一个强大特性,它允许开发人员轻松地为现有类型添加新功能,并广泛用于应用程序中以简化代码和提高代码可读性。
您可以阅读这些文章 C# 中的扩展方法,了解有关扩展方法的更多详细信息。
在 C# 中,Dispose 和 Finalize 方法都用于释放资源,但它们用于不同的目的和行为。
Dispose 方法释放未由 .NET 运行时自动管理的非托管资源,例如文件句柄或数据库连接。它通常在实现 IDisposable 接口的类中实现,该接口定义了 Dispose 方法。
Dispose 方法由客户端代码显式调用以在不再需要时释放资源。可以使用语句隐式调用它,确保在对象超出范围时调用 Dispose 方法。
另一方面,Finalize 方法用于在对象被垃圾回收之前对其执行清理操作。因此,它通常在重写 Object.Finalize 方法的类中实现。
垃圾收集器调用 Finalize 方法自动管理 .NET 对象的内存,以释放未被 Dispose 方法显式释放的非托管资源。
这两种方法的主要区别在于 Dispose 方法是确定性的,可以由客户端代码显式调用。相比之下,Finalize 方法是不确定的,由垃圾收集器在不确定的时间调用。
重要的是要注意,实现 Dispose 方法的对象也应该实现 Finalize 方法作为备份机制,以防客户端代码不调用 Dispose 方法。
总之,Dispose 方法用于确定性地释放非托管资源。相反,Finalize 方法用作备份机制,以在对象被垃圾回收时释放非托管资源。
StringBuilder 和 string 用于字符串值,但两者在实例创建和性能方面有很多差异。
细绳
字符串是不可变对象。不可变是指当我们在代码中创建字符串对象时,我们不能在任何操作中修改或更改该对象,例如插入新值或用字符串对象中的现有值替换或附加任何值。当我们要做一些改变字符串的操作时,它会简单地处理字符串对象的旧值,并在内存中创建一个新实例来保存字符串对象中的新值,例如:
笔记
字符串生成器
System.Text.StringBuilder 是一个包含字符串值的可变对象;可变意味着一旦我们创建了一个 System.Text.Stringbuilder 对象。我们可以将此对象用于任何操作,例如使用插入函数在现有字符串中插入值以及替换或追加,而无需每次都创建 System.Text.StringBuilder 的新实例,因此它使用的是前一个对象。这样,与 System.String 相比,它的工作速度更快。让我们看一个例子来理解 System.Text.StringBuilder。
笔记
委托是一个或多个函数指针的抽象(存在于 C++ 中;对此的解释超出了本文的范围)。.NET 以委托的形式实现了函数指针的概念。使用委托,您可以将函数视为数据。委托允许函数作为参数传递,作为值从函数返回,并存储在数组中。代表有以下特点:
委托包含几个有用的字段。第一个保存对象的引用,第二个保存方法指针。调用委托时,会在包含的引用上调用实例方法。但是,如果对象引用为 null,则运行时会将其理解为该方法是静态方法。此外,在语法上调用委托与调用常规函数相同。因此,委托非常适合实现回调。
为什么我们需要代表?
历史上,Windows API 经常使用 C 风格的函数指针来创建回调函数。使用回调,程序员能够配置一个函数以向应用程序中的另一个函数报告。所以使用回调的目的是处理按钮单击、菜单选择和鼠标移动活动。但这种传统方法的问题在于回调函数不是类型安全的。在 .NET 框架中,回调仍然可以通过更有效的方法使用委托。但是,委托人维护三个重要信息:
委托是一种解决方案,适用于您希望将方法传递给其他方法的情况。您已经习惯于将数据作为参数传递给方法,以至于将方法作为参数而不是数据传递的想法听起来很奇怪。但是,在某些情况下,您有一个方法可以执行某些操作,例如,调用其他方法。您在编译时不知道第二种方法是什么。该信息仅在运行时可用。因此,代表是克服这些复杂情况的工具。
密封类用于限制面向对象编程的继承特性。一旦一个类被定义为密封类,该类就不能被继承。
在 C# 中,sealed 修饰符将类定义为密封的。在 Visual Basic .NET 中,Not Inheritable 关键字用于密封类。如果类派生自密封类,编译器会抛出错误。
如果您曾经注意到,结构是密封的。您不能从结构派生类。
以下类定义在 C# 中定义了一个密封类:
// Sealed class
sealed class SealedClass
{
}
分部类仅用于将一个类的定义拆分为同一个源代码文件或多个源文件中的两个或多个类。您可以在多个文件中创建一个类定义,这些文件将在运行时被编译为一个类。此外,当您创建此类的实例时,您可以使用同一对象访问所有源文件中的所有方法。
部分类可以在同一个命名空间中创建。但是,不可能在不同的命名空间中创建分部类。因此,使用“partial”关键字将所有要绑定的类名与同一命名空间中的类的相同名称结合使用。让我们看一个例子:
装箱和拆箱都用于类型转换,但它们有一些区别:
拳击
装箱是将一个值类型的数据类型转换为该值类型实现的对象或任何接口数据类型。例如,当CLR boxes时,一个值意味着当CLR将一个值类型转换为Object Type时,它将值包装在一个System.Object中并存储在应用程序域的堆区域中。
例子
开箱** **
拆箱也是从对象或任何已实现的接口类型中提取值类型的过程。装箱可以隐式完成,但拆箱必须通过代码显式进行。
示例:** **
装箱和拆箱的概念是 C# 类型系统统一视图的基础,在这种系统中,任何类型的值都可以被视为一个对象。
IEnumerable 是 System.Collections 命名空间中所有非泛型集合的父接口,如 ArrayList、HastTable 等可以枚举的集合。此接口的通用版本是 IEnumerable<T>,它是 System.Collections.Generic 命名空间(如 List<> 等)中所有通用集合类的父接口。
在 System.Collections.Generic.IEnumerable<T> 中只有一个方法 GetEnumerator(),它返回一个 IEnumerator。IEnumerator 提供了通过公开 Current 属性以及 Move Next 和 Reset 方法来遍历集合的能力,如果我们没有这个接口作为父级的话,所以我们不能通过 foreach 循环使用迭代或者不能使用那个类对象在我们的 LINQ 查询中。
Early Binding和Late Binding概念属于C#中的多态性。多态性是面向对象编程的特征,它允许一种语言以不同的形式使用相同的名称。例如,名为 Add 的方法可以添加整数、双精度和小数。
多态性我们有两种不同的类型来实现:
编译时多态性或早期绑定** **
在编译时多态性或早期绑定中,我们将使用多个具有相同名称但参数类型或参数数量不同的方法。正因为如此,我们可以在同一个类中使用相同的方法名执行不同的任务,也称为方法重载。
在以下示例中查看我们如何做到这一点:
运行时多态性或后期绑定
运行时多态性也称为后期绑定。在运行时多态或后期绑定中,我们可以使用具有相同签名的相同方法名称,这意味着相同类型或数量的参数,但不能在同一类中,因为编译器在编译时不允许这样做。因此,当实例化子对象或派生类对象时,我们可以在派生类中的运行时使用该绑定。这就是我们称之为后期绑定的原因。我们必须将我的父类函数创建为部分函数,并在驱动程序或子类中将其创建为带有 override 关键字的覆盖函数。
例子
在深入探讨差异之前,让我们了解什么是 IEnumerable 和 IQueryable。
可枚举
它是 System.Collections 命名空间中所有非泛型集合的父接口,如 ArrayList、HastTable 等,可以枚举。此接口的通用版本是 IEnumerable<T>,它是 System.Collections.Generic 命名空间中所有通用集合类的父接口,如 List<> 等。
我可查询** **
根据 MSDN,IQueryable 接口旨在由查询提供程序实现。因此,它应该只由也实现了 IQueryable<T> 的提供者来实现。如果提供程序未同时实现 IQueryable<T>,则无法在提供程序的数据源上使用标准查询运算符。
IQueryable 接口继承了 IEnumerable 接口,因此如果它表示查询,则可以枚举该查询的结果。枚举导致与 IQueryable 对象关联的表达式树被执行。“执行表达式树”的定义特定于查询提供程序。例如,它可能涉及将表达式树转换为适用于基础数据源的查询语言。不返回可枚举结果的查询在调用 Execute 方法时执行。
如果我们在同一个类中实现多个方法名称冲突的接口,则不需要全部定义。换句话说,我们可以说,如果我们在同一个类中有冲突的方法,我们不能在同一个类中独立实现它们的方法体,因为它们同名同签名。因此,我们必须在方法名之前使用接口名来解除这个方法没收。让我们看一个例子:
interface testInterface1 {
void Show();
}
interface testInterface2 {
void Show();
}
class Abc: testInterface1,
testInterface2 {
void testInterface1.Show() {
Console.WriteLine("For testInterface1 !!");
}
void testInterface2.Show() {
Console.WriteLine("For testInterface2 !!");
}
}
现在看看如何在类中使用它们:
class Program {
static void Main(string[] args) {
testInterface1 obj1 = new Abc();
testInterface1 obj2 = new Abc();
obj1.Show();
obj2.Show();
Console.ReadLine();
}
}
输出
在 C# 中,数组索引从零开始。这意味着数组的第一项从第 0 个位置开始。因此,数组中最后一项的位置将使项数总数为 - 1。因此,如果数组有十项,则前 10 项位于第 9 位。
在 C# 中,数组可以声明为固定长度或动态。
固定长度数组 可以存储预定义数量的项目。
动态数组没有 预定义的大小。相反,动态数组的大小 会随着您向数组中添加新项而增加。您可以声明一个固定长度或动态的数组。您甚至可以在定义动态数组后将其更改为静态数组。
让我们看一下 C# 中数组的简单声明。以下代码片段定义了最简单的没有固定大小的整数类型动态数组。
整数 [] 整数数组;
从上面的代码片段可以看出,数组的声明以一种数组类型开始,后跟方括号([])和数组名称。
以下代码片段声明了一个只能存储五个项目的数组,从索引 0 到 4 开始。
int[] intArray;
intArray = new int[5];
以下代码片段声明了一个数组,该数组可以存储从索引 0 到 99 的 100 个项目。
int[] intArray;
intArray = new int[100];
构造函数链接是一种将两个或多个类连接为继承关系的方法。在Constructor Chaining中,每个子类的构造函数都通过base关键字隐式映射到一个父类的Constructor,所以当你创建子类的实例时,它会调用父类的Constructor。没有它,继承是不可能的。
Array.Clone() 方法创建数组的浅表副本。Array 的浅拷贝只复制 Array 的元素,无论是引用类型还是值类型,但它不复制引用所引用的对象。新数组中的引用指向与原始数组中相同的对象。
Array 类的 CopyTo() 静态方法将一个数组的一部分复制到另一个数组。CopyTo 方法将一个数组的所有元素复制到另一个一维数组。例如,清单九中列出的代码将整数数组的内容复制到各种对象类型。
我们可以在一个 try 语句中使用多个 catch 块。这是因为每个 catch 块都可以捕获不同的异常。以下代码示例显示如何使用单个 try 语句实现多个 catch 语句。
using System;
class MyClient {
public static void Main() {
int x = 0;
int div = 0;
try {
div = 100 / x;
Console.WriteLine("Not executed line");
} catch (DivideByZeroException de) {
Console.WriteLine("DivideByZeroException");
} catch (Exception ee) {
Console.WriteLine("Exception");
} finally {
Console.WriteLine("Finally Block");
}
Console.WriteLine("Result is {0}", div);
}
}
单例设计模式是一种创建型设计模式,可确保一个类只有一个实例,并提供对该实例的全局访问点。此外,此模式控制对象创建,将可创建的实例数限制为单个实例,该实例在整个应用程序中共享。
在典型的 Singleton 实现中,Singleton 类有一个防止直接实例化的私有构造函数和一个返回类的单个实例的静态方法。第一次调用静态方法时,它会创建该类的一个新实例并将其存储在私有静态变量中。对静态方法的后续调用返回相同的实例。
例如,考虑用于管理数据库连接的 Singleton 类 DatabaseConnection。该类可以这样实现:
public class DatabaseConnection
{
private static DatabaseConnection instance;
private DatabaseConnection() { }
public static DatabaseConnection GetInstance()
{
if (instance == null)
instance = new DatabaseConnection();
return instance;
}
// Database connection methods
public void Connect() { /* ... */ }
public void Disconnect() { /* ... */ }
}
在此实现中,GetInstance 方法用于创建或检索类的单个实例。构造函数是私有的,所以不能从类外部直接实例化类。
单例广泛用于管理创建成本高或可用性有限的资源的应用程序,例如数据库连接、网络套接字、日志系统等。
重要的是要注意,单例类可能会引入潜在的问题,例如全局状态和单元测试困难,因此应谨慎使用,并且仅在必要时才使用。
基本区别在于 Throw 异常会覆盖堆栈跟踪。很难找到抛出异常的原始代码行号。
throw 基本上保留了堆栈信息,并在抛出的异常中将堆栈信息添加到堆栈信息中。
让我们看看更好地理解差异意味着什么。我正在使用一个控制台应用程序来轻松测试并查看两者的用法在功能上有何不同。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestingThrowExceptions {
class Program {
public void ExceptionMethod() {
throw new Exception("Original Exception occurred in ExceptionMethod");
}
static void Main(string[] args) {
Program p = new Program();
try {
p.ExceptionMethod();
} catch (Exception ex) {
throw ex;
}
}
}
现在按 F5 键运行代码,看看会发生什么。它返回异常并查看堆栈跟踪。
C# 引入了一个称为索引器的新概念,用于将对象视为数组。索引器在 C# 中通常称为智能数组。但是,它们不是面向对象编程的重要组成部分。
定义索引器允许您创建充当虚拟数组的类。然后,可以使用 [] 数组访问运算符访问该类的实例。
创建索引器
< modifier > <
return type > this[argument list] {
get {
// your get block code
}
set {
// your set block code
}
}
在上面的代码中,
<修饰符>
它可以是私有的、公共的、受保护的或内部的。
<返回类型>
它可以是任何有效的 C# 类型。
委托是 .NET 中的一种基本类型。Delegate 是一个用于在运行时创建和调用委托的类。
C# 中的委托允许开发人员将方法视为对象并从他们的代码中调用它们。
实施多播委托示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
delegate void MDelegate();
class DM {
static public void Display() {
Console.WriteLine("Meerut");
}
static public void print() {
Console.WriteLine("Roorkee");
}
}
class MTest {
public static void Main() {
MDelegate m1 = new MDelegate(DM.Display);
MDelegate m2 = new MDelegate(DM.print);
MDelegate m3 = m1 + m2;
MDelegate m4 = m2 + m1;
MDelegate m5 = m3 - m2;
m3();
m4();
m5();
}
}
== 运算符和 Equals() 方法比较两个值类型数据项或引用类型数据项。相等运算符 (==) 是比较运算符,Equals() 方法比较字符串的内容。== 运算符比较引用标识,而 Equals() 方法只比较内容。让我们看一些例子。
在这个例子中,我们将一个字符串变量分配给另一个变量。字符串是引用类型。因此,在下面的示例中,一个字符串变量被分配给另一个字符串变量,在堆中引用相同的标识,并且两者具有相同的内容。因此,您将获得 == 运算符和 Equals() 方法的 True 输出。
using System;
namespace ComparisionExample {
class Program {
static void Main(string[] args) {
string name = "sandeep";
string myName = name;
Console.WriteLine("== operator result is {0}", name == myName);
Console.WriteLine("Equals method result is {0}", name.Equals(myName));
Console.ReadKey();
}
}
}
“是”运算符
在C#语言中,我们使用“is”运算符来检查对象类型。如果两个对象属于同一类型,则返回true;否则,它返回 false。
让我们在 C# 代码中理解这一点。首先,我们声明两个类,Speaker 和 Author。
class Speaker {
public string Name {
get;
set;
}
}
class Author {
public string Name {
get;
set;
}
}
现在,让我们创建一个 Speaker 类型的对象:
var speaker = new Speaker { Name="Gaurav Kumar Arora"};
现在,让我们检查对象是否为 Speaker 类型:
var isTrue = speaker is Speaker;
在前面,我们正在检查匹配类型。所以,是的,我们的扬声器是 Speaker 类型的对象。
Console.WriteLine("speaker is of Speaker type:{0}", isTrue);
所以,结果是真的。
但是,这里我们得到错误的:
var author = new Author { Name = "Gaurav Kumar Arora" };
var isTrue = speaker is Author;
Console.WriteLine("speaker is of Author type:{0}", isTrue);
因为我们的speaker不是Author类型的对象。
“作为”运算符
“as”运算符的行为类似于“is”运算符。唯一的区别是如果两者都与该类型兼容,它会返回对象。否则返回空值。
让我们在 C# 代码中理解这一点。
public static string GetAuthorName(dynamic obj)
{
Author authorObj = obj as Author;
return (authorObj != null) ? authorObj.Name : string.Empty;
}
我们有一个接受动态对象的方法,如果对象是 Author 类型,则返回对象名称属性。
在这里,我们声明了两个对象:
var speaker = new Speaker { Name="Gaurav Kumar Arora"};
var author = new Author { Name = "Gaurav Kumar Arora" };
以下返回“名称”属性:
var authorName = GetAuthorName(author);
Console.WriteLine("Author name is:{0}", authorName);
它返回一个空字符串:
authorName = GetAuthorName(speaker);
Console.WriteLine("Author name is:{0}", authorName);
可空类型是包含已定义数据类型或空值的数据类型。
此可空类型概念与“var”不兼容。
在运算符“?”的帮助下,任何数据类型都可以声明为可空类型。
例如,以下代码将 int 'i' 声明为 null。
int? i = null;
如前一节所述,“var”与可空类型不兼容。所以,如果你声明以下内容,你会得到一个错误。
var? i = null;
方法重载是一种实现编译时多态性的方法,我们可以使用具有相同名称但不同签名的方法。例如,以下代码示例的方法卷具有三种不同的签名,具体取决于参数和返回值的数量和类型。
例子
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Hello_Word {
class overloding {
public static void Main() {
Console.WriteLine(volume(10));
Console.WriteLine(volume(2.5F, 8));
Console.WriteLine(volume(100L, 75, 15));
Console.ReadLine();
}
static int volume(int x) {
return (x * x * x);
}
static double volume(float r, int h) {
return (3.14 * r * r * h);
}
static long volume(long l, int b, int h) {
return (l * b * h);
}
}
}
笔记
假设我们有一个带有两个参数对象类型的方法和一个带有两个整数参数的同名方法,当我们用一个 int 值调用该方法时。在这种情况下,它将使用整数参数而不是对象类型参数方法调用该方法。
.NET 中的对象池允许对象保留在内存池中,因此可以重用对象而无需重新创建它们。本文解释了 .NET 中的对象池以及如何在 C# 中实现对象池。
这是什么意思?
对象池是准备使用的对象的容器。每当有对新对象的请求时,池管理器将接受该请求,该请求将通过从池中分配一个对象来满足。
它是如何工作的?
为此,我们将使用工厂模式。我们将有一个工厂方法,它将负责对象的创建。每当请求新对象时,工厂方法都会查看对象池(我们使用 Queue 对象)。如果在允许的范围内有任何对象可用,它将返回该对象(值对象)。否则,将创建一个新对象并返回给您。
泛型允许您延迟类或方法中编程元素的数据类型的规范,直到在程序中使用它。换句话说,泛型允许您编写可以使用任何数据类型的类或方法。
您编写类或方法的规范,使用数据类型的替代参数。当编译器遇到类的构造函数或方法的函数调用时,它会生成处理特定数据类型的代码。
通用类和方法结合了可重用性、类型安全性和效率,这是非通用类和方法无法做到的。泛型最常用于集合和操作它们的方法。.NET Framework 类库的 2.0 版提供了一个新的命名空间 System.Collections.Generic,其中包含几个新的基于泛型的集合类。建议所有以 .NET Framework 2.0 及更高版本为目标的应用程序都使用新的泛型集合类,而不是旧的非泛型对应类,例如 ArrayList。
泛型的特点
泛型是一种通过以下方式丰富您的程序的技术:
访问修饰符是用于指定成员或类型声明的可访问性的关键字。
访问修饰符是用于指定类型成员或类型本身的可访问范围的关键字。例如,公共类可供全世界访问,而内部类可能仅供程序集访问。
为什么要使用访问修饰符?
访问修饰符是面向对象编程的一个组成部分。访问修饰符用于实现对 OOP 的封装。此外,访问修饰符允许您定义谁可以或不可以访问某些功能。
在 C# 中,有六种不同类型的访问修饰符:
修改器 | 描述 |
民众 | 对访问公共成员没有限制。 |
私人的 | 访问仅限于类定义内。如果没有正式指定,这是默认的访问修饰符类型 |
受保护 | 访问仅限于类定义和从该类继承的任何类 |
内部的 | 访问仅限于当前项目程序集中定义的类 |
受保护的内部 | 访问仅限于当前程序集和从包含类派生的类型。当前项目和派生类中的所有成员都可以访问这些变量。 |
私人保护 | 访问仅限于当前程序集中的包含类或从包含类派生的类型。 |
虚方法是可以在派生类中重新定义的方法。虚方法在基类中有一个实现,并从该类派生。当方法的基本功能相同时使用它,但有时在派生类中需要更多功能。在基类中创建了一个虚拟方法,可以在派生类中覆盖它。我们使用 virtual 关键字在基类中创建一个虚方法,并使用 override 关键字在派生类中重写该方法。
当一个方法在基类中声明为虚方法时,它可以在基类中定义,并且派生类可以选择重写该方法。覆盖方法还为一种方法提供了不止一种形式。因此,它也是多态性的一个例子。
当一个方法在基类中被声明为虚方法并且在派生类中具有相同的定义时,则不需要在派生类中重写它。但是当一个虚方法在基类和派生类中有不同的定义时,那么就需要在派生类中重写它。
调用虚拟方法时,将检查对象的运行时类型以查找覆盖成员。首先,调用最派生类中的覆盖成员,如果没有派生类覆盖该成员,则该成员可能是原始成员。
虚拟方法
在此处了解有关 C# 中的虚拟方法的更多信息, C# 中的虚拟方法。
以下是两者之间的差异列表:
更正:回复:#40 -- ArrayList 不使用LinkedList -- 它使用动态改变大小的后备数组。看评论。
在C#中,数据类型可以有两种类型,值类型和引用类型。值类型变量直接包含它们的对象(或数据)。如果我们将一个值类型变量复制到另一个,我们为第二个变量复制了一个东西。它们都将独立地对其值进行操作,值类型数据类型存储在堆栈中,引用数据类型存储在堆中。
在C#中,基本数据类型包括int、char、bool、long,它们都是值类型。此外,类和集合是引用类型。
C# 中的序列化将对象转换为字节流,以将对象存储在内存、数据库或文件中。它的主要目的是保存对象的状态,以便在需要时能够重新创建它。相反的过程称为反序列化。
序列化分为三种类型,
在 C# 中有两种使用 using 关键字的方法。一个是指令,另一个是声明。让我们解释一下!
通常,我们在代码隐藏和类文件中使用 using 关键字来添加命名空间。然后它使当前页面上的所有类、接口和抽象类及其方法和属性可用。添加命名空间可以通过以下两种方式完成:
这是在 C# 中使用 using 关键字的另一种方法。它在提高垃圾收集性能方面起着至关重要的作用。
锯齿状数组是其元素为数组的数组。交错数组的元素可以具有不同的维度和大小。锯齿状数组有时称为“数组的数组”。
C#中引入了一种特殊类型的数组。交错数组是数组的数组,其中每个数组索引的长度可以不同。
例子
int[][] jagArray = new int[5][];
在上面的声明中,行的大小是固定的。但未指定列,因为它们可能会有所不同。
声明和初始化锯齿状数组。
int[][] jaggedArray = new int[5][];
jaggedArray[0] = new int[3];
jaggedArray[1] = new int[5];
jaggedArray[2] = new int[2];
jaggedArray[3] = new int[8];
jaggedArray[4] = new int[10];
jaggedArray[0] = new int[] { 3, 5, 7, };
jaggedArray[1] = new int[] { 1, 0, 2, 4, 6 };
jaggedArray[2] = new int[] { 1, 6 };
jaggedArray[3] = new int[] { 1, 0, 2, 4, 6, 45, 67, 78 };
jaggedArray[4] = new int[] { 1, 0, 2, 4, 6, 34, 54, 67, 87, 78 };
多线程允许程序同时运行多个线程。本文解释了多线程在 .NET 中的工作原理。本文涵盖了线程领域的全部内容,从线程创建、竞争条件、死锁、监视器、互斥量、同步、信号量等。
线程的真正用途不是单个顺序线程,而是在单个程序中使用多个线程。多个线程同时运行并执行各种任务称为多线程。线程被认为是轻量级进程,因为它在程序的上下文中运行并利用为该程序分配的资源。
单线程进程只包含一个线程,而多线程进程包含多个线程执行。
匿名类型允许我们在不定义它们的情况下创建新类型。这是一种在单个对象中定义只读属性而无需显式定义每种类型的方法。在这里,Type 由编译器生成,并且只能为当前代码块访问。属性的类型也由编译器推断。
我们可以使用“new”关键字和对象初始值设定项来创建匿名类型。
例子
var anonymousData = new
{
ForeName = "Jignesh",
SurName = "Trivedi"
};
Console.WriteLine("First Name : " + anonymousData.ForeName);
使用 LINQ 示例的匿名类型
匿名类型还与 LINQ 查询表达式的“Select”子句一起使用,以返回属性的子集。
例子
如果任何对象集合具有调用 FirstName、LastName、DOB 等的属性...并且您在查询数据后只需要 FirstName 和 LastName,则:
class MyData {
public string FirstName {
get;
set;
}
public string LastName {
get;
set;
}
public DateTime DOB {
get;
set;
}
public string MiddleName {
get;
set;
}
}
static void Main(string[] args) {
// Create Dummy Data to fill Collection.
List < MyData > data = new List < MyData > ();
data.Add(new MyData {
FirstName = "Jignesh", LastName = "Trivedi", MiddleName = "G", DOB = new DateTime(1990, 12, 30)
});
data.Add(new MyData {
FirstName = "Tejas", LastName = "Trivedi", MiddleName = "G", DOB = new DateTime(1995, 11, 6)
});
data.Add(new MyData {
FirstName = "Rakesh", LastName = "Trivedi", MiddleName = "G", DOB = new DateTime(1993, 10, 8)
});
data.Add(new MyData {
FirstName = "Amit", LastName = "Vyas", MiddleName = "P", DOB = newDateTime(1983, 6, 15)
});
data.Add(new MyData {
FirstName = "Yash", LastName = "Pandiya", MiddleName = "K", DOB = newDateTime(1988, 7, 20)
});
}
var anonymousData = from pl in data
select new {
pl.FirstName, pl.LastName
};
foreach(var m in anonymousData) {
Console.WriteLine("Name : " + m.FirstName + " " + m.LastName);
}
}
哈希表是存储 (Keys, Values) 对的集合。在这里,Keys 用于查找存储位置,是不可变的,并且在 Hashtable 中不能有重复的条目。.Net Framework 提供了一个哈希表类,它包含实现哈希表所需的所有功能,无需任何额外开发。哈希表是一个通用的字典集合。集合中的每个项目都是一个具有两个属性的 DictionaryEntry 对象:一个键对象和一个值对象。这些被称为键/值。将项目添加到哈希表时会自动生成哈希码。此代码对开发人员隐藏。使用用于标识的键对象来访问表的值。由于集合中的项目是根据隐藏的哈希码排序的,
哈希表集合
基类库提供在 System.Collections 命名空间中定义的哈希表类,因此您不必编写自己的哈希表。相反,它会处理您每次添加的散列的每个键,然后使用散列码非常快速地查找元素。哈希表的容量是哈希表可以容纳的元素的数量。当元素添加到哈希表时,容量会根据需要通过重新分配自动增加。它是一种较旧的 .Net Framework 类型。
声明一个哈希表
Hashtable 类通常位于名为 System.Collections 的命名空间中。所以要执行任何示例,我们必须添加 using System.Collections; 到源代码。哈希表的声明是:
Hashtable HT = new Hashtable ();
LINQ 代表语言集成查询。LINQ 是一种数据查询方法,它使用类似于 SQL 查询的语法为 .NET 语言提供功能。
LINQ 具有查询任何数据源的强大功能。数据源可以是对象、数据库或 XML 文件的集合。我们可以轻松地从任何实现 IEnumerable<T> 接口的对象中检索数据。
LINQ的优势
它允许您以应用程序的本地语言(如 VB 或 C#)查询数组、可枚举类等集合,其方式与使用 SQL 查询数据库的方式大致相同。
System.IO 命名空间提供了四个类,允许您操作单个文件并与机器目录结构交互。Directory 和File 直接扩展System.Object 支持使用各种静态方法创建、复制、移动和删除文件。但是,它们只包含静态方法,从不实例化。FileInfo 和 DirecotryInfo 类型派生自抽象类 FileSystemInfo 类型。它们通常用于获取文件或目录的完整详细信息,因为它们的成员倾向于返回强类型对象。它们实现与目录和文件大致相同的公共方法,但它们是有状态的,并且这些类的成员不是静态的。
反射是运行时类型发现的过程,用于检查元数据、CIL 代码、后期绑定和自生成代码。在运行时,通过使用反射,我们可以访问 ildasm 实用程序在设计时显示的相同“类型”信息。反射类似于逆向工程,在逆向工程中我们可以破坏现有的.exe 或.dll 程序集以探索定义的重要内容信息,包括方法、字段、事件和属性。
您可以使用 System.Reflection 命名空间动态发现给定类型支持的接口集。
反射通常用于转储加载的程序集列表、它们对检查方法、属性等的引用。Reflection也用在Reflector、Fxcop、NUnit等外部反汇编工具中,因为.NET工具不需要解析源代码,类似于C++。
元数据调查
下面的程序通过创建一个基于控制台的应用程序来描述反射的过程。该程序将显示 mscorlib.dll 程序集中任何类型的字段、方法、属性和接口的详细信息。在继续之前,必须导入“System.Reflection”。
这里,我们在程序类中定义了几个静态方法来枚举指定类型中的字段、方法和接口。静态方法采用单个“System.Type”参数并返回 void。
static void FieldInvestigation(Type t) {
Console.WriteLine("*********Fields*********");
FieldInfo[] fld = t.GetFields();
foreach(FieldInfo f in fld) {
Console.WriteLine("-->{0}", f.Name);
}
}
static void MethodInvestigation(Type t) {
Console.WriteLine("*********Methods*********");
MethodInfo[] mth = t.GetMethods();
foreach(MethodInfo m in mth) {
Console.WriteLine("-->{0}", m.Name);
}
我希望这 50 个 C# 面试问题及其答案能帮助您通过下一次 C# 和 .NET 面试。这里有一些更多的面试问题和答案。
文章原文出处: https: //www.c-sharpcorner.com
1624240146
C and C++ are the most powerful programming language in the world. Most of the super fast and complex libraries and algorithms are written in C or C++. Most powerful Kernel programs are also written in C. So, there is no way to skip it.
In programming competitions, most programmers prefer to write code in C or C++. Tourist is considered the worlds top programming contestant of all ages who write code in C++.
During programming competitions, programmers prefer to use a lightweight editor to focus on coding and algorithm designing. Vim, Sublime Text, and Notepad++ are the most common editors for us. Apart from the competition, many software developers and professionals love to use Sublime Text just because of its flexibility.
I have discussed the steps we need to complete in this blog post before running a C/C++ code in Sublime Text. We will take the inputs from an input file and print outputs to an output file without using freopen
file related functions in C/C++.
#cpp #c #c-programming #sublimetext #c++ #c/c++
1597937354
If you are familiar with C/C++then you must have come across some unusual things and if you haven’t, then you are about to. The below codes are checked twice before adding, so feel free to share this article with your friends. The following displays some of the issues:
The below code generates no error since a print function can take any number of inputs but creates a mismatch with the variables. The print function is used to display characters, strings, integers, float, octal, and hexadecimal values onto the output screen. The format specifier is used to display the value of a variable.
A signed integer is a 32-bit datum that encodes an integer in the range [-2147483648 to 2147483647]. An unsigned integer is a 32-bit datum that encodes a non-negative integer in the range [0 to 4294967295]. The signed integer is represented in twos-complement notation. In the below code the signed integer will be converted to the maximum unsigned integer then compared with the unsigned integer.
#problems-with-c #dicey-issues-in-c #c-programming #c++ #c #cplusplus
1589816580
In this article, we’ll take a look at using the isdigit() function in C/C++. This is a very simple way to check if any value is a digit or not. Let’s look at how to use this function, using some simple examples.
#c programming #c++ #c #c#
1590587580
In this Video We are going to see how to use Loops in C++. We will see How to use For, While, and Do While Loops in C++.
C++ is general purpose, compiled, object-oriented programming language and its concepts served as the basis for several other languages such as Java, Python, Ruby, Perl etc.
#c #c# #c++ #programming-c
1589791833
C++ is general purpose, compiled, object-oriented programming language and its concepts served as the basis for several other languages such as Java, Python, Ruby, Perl etc.
The goal of this course is to provide you with a working knowledge of C++. We’ll start with the basics, including syntax, operators, loops, and functions. This Course will explain you how to use data structures and create your own Functions. This Course will show you the details of the powerful object and template systems so you can create useful classes and objects.
Youtube channel: ProgrammingKnowledge - https://www.youtube.com/watch?v=_SH1T3y_D7o
#c #c# #c++ #programming-c