软件开发面试题(C#语言,.NET框架)

news2024/12/24 11:33:47

1. 解释什么是委托(Delegate),并举例说明它在C#中的用法。

        委托是一种引用类型,它可以用于封装一个或多个方法。委托对象可以像方法一样调用,甚至可以用于创建事件处理程序。委托是C#中实现事件和回调函数的重要机制。

委托的基本语法:

  1. 定义委托
    public delegate void MyDelegate(string message);
  2. 创建委托实例
    MyDelegate del = new MyDelegate(MethodToCall);
  3. 调用委托
    del("Hello, World!");

 示例:

using System;

public class Program
{
    // Step 1: Define a delegate
    public delegate void MyDelegate(string message);

    // Step 2: Create a method that matches the delegate signature
    public static void MethodToCall(string message)
    {
        Console.WriteLine(message);
    }

    public static void Main()
    {
        // Step 3: Create an instance of the delegate
        MyDelegate del = new MyDelegate(MethodToCall);

        // Step 4: Call the delegate
        del("Hello, World!");
    }
}

在这个示例中,定义了一个委托MyDelegate,它接受一个字符串参数。还定义了一个方法MethodToCall,它与委托的签名匹配。然后,创建了一个MyDelegate实例,并调用它。

委托的应用场景

委托在C#中常用于事件处理、回调函数和实现可插拔的代码模式等场景中。

  1. 事件处理 委托用于定义事件处理程序。C#中的事件机制就是基于委托的。

  2. 回调 委托可以用来实现回调函数。比如,你可以将一个方法传递给另一个方法,以便在特定条件下调用。

  3. 多播委托 一个委托实例可以封装多个方法。这些方法会按顺序执行,这在实现事件广播时非常有用。

  4. 委托的定义和使用

  5. 定义委托:委托通过delegate关键字定义。委托类型声明中指定了方法的签名(返回类型和参数类型)。

    // 定义一个委托类型
    public delegate void MyDelegate(int x, int y);
    
    // 另一种方式,使用系统定义的委托类型
    Action<int, int> myAction = (x, y) => Console.WriteLine(x + y);
    

  6. 实例化委托:创建委托实例并指定要调用的方法。

    public class Calculator
    {
        // 委托实例作为类的字段
        public MyDelegate AddNumbers;
        
        public void PerformAddition(int x, int y)
        {
            // 调用委托实例,触发绑定的方法
            AddNumbers?.Invoke(x, y);
        }
    }
    
  7. 使用委托:将方法绑定到委托实例,然后通过委托实例调用方法。

    class Program
    {
        static void Main()
        {
            Calculator calc = new Calculator();
            
            // 绑定方法到委托实例
            calc.AddNumbers = (a, b) => Console.WriteLine($"Sum: {a + b}");
            
            // 调用委托实例触发绑定的方法
            calc.PerformAddition(5, 3); // 输出:Sum: 8
        }
    }
    
  8. 在上面的示例中:

    MyDelegate委托类型定义了可以接受两个int参数并且不返回任何内容的方法。Calculator类包含一个AddNumbers委托实例作为其字段,并且有一个方法PerformAddition来调用委托实例。在Main方法中,我们创建了一个Calculator对象,并将一个lambda表达式绑定到AddNumbers委托实例上。最后,调用PerformAddition方法来触发绑定的方法。

2.解释一下C#中的异步编程以及async和await关键字的用法。

异步编程的概念

异步编程是一种编程范式,允许程序在执行耗时操作(如I/O操作、网络请求等)时,不会阻塞主线程或用户界面线程。这样可以提高应用程序的响应性和性能。

asyncawait 关键字

C#引入了asyncawait关键字来简化异步编程,使代码更加简洁和易于理解。

  1. async 关键字

    async关键字用于标记一个方法为异步方法。一个异步方法可以包含一个或多个await表达式。

  2. 用于修饰方法,表示该方法包含异步操作。
  3. async方法可以返回TaskTask<T>void(仅用于事件处理程序)。
  4. await 关键字

    await关键字用于暂停异步方法的执行,直到被等待的任务完成。它会释放当前线程,使其可以执行其他任务。

  5. 用于等待一个异步操作的完成。
  6. await关键字只能在async方法中使用。
  7. 当使用await等待一个任务时,控制权会返回给调用者,允许其他操作继续进行。
    using System;
    using System.Net.Http;
    using System.Threading.Tasks;
    
    class Program
    {
        static async Task Main(string[] args)
        {
            // 调用异步方法
            string content = await FetchDataFromUrlAsync("https://jsonplaceholder.typicode.com/todos/1");
            Console.WriteLine(content);
        }
    
        // 定义一个异步方法
        static async Task<string> FetchDataFromUrlAsync(string url)
        {
            using (HttpClient client = new HttpClient())
            {
                // 发送异步请求并等待响应
                HttpResponseMessage response = await client.GetAsync(url);
                response.EnsureSuccessStatusCode();
                
                // 读取响应内容
                string responseBody = await response.Content.ReadAsStringAsync();
                return responseBody;
            }
        }
    }
    
    /*
    Main方法被标记为async,并返回一个Task。
    FetchDataFromUrlAsync方法被标记为async,并返回一个Task<string>。
    在FetchDataFromUrlAsync方法中,使用await等待HttpClient.GetAsync和response.Content.ReadAsStringAsync的完成。
    await关键字允许程序在等待任务完成时,继续处理其他操作,从而提高应用程序的效率和响应性。
    */

  8. using System;
    using System.Net.Http;
    using System.Threading.Tasks;
    
    public class Program
    {
        public static async Task Main(string[] args)
        {
            Console.WriteLine("Starting download...");
            string result = await DownloadContentAsync("https://www.example.com");
            Console.WriteLine("Download completed.");
            Console.WriteLine(result);
        }
    
        public static async Task<string> DownloadContentAsync(string url)
        {
            using (HttpClient client = new HttpClient())
            {
                string content = await client.GetStringAsync(url);
                return content;
            }
        }
    }
    

解释

  1. Main 方法

    我们在Main方法中调用了一个异步方法DownloadContentAsync并使用await关键字等待其完成。

  2. DownloadContentAsync 方法

    这个方法被标记为async,表示它是一个异步方法。我们使用HttpClient类发送HTTP请求,并使用await关键字等待GetStringAsync方法完成。GetStringAsync方法是一个异步方法,它会返回一个Task<string>

  3. await 关键字

    await关键字会暂停DownloadContentAsync方法的执行,直到GetStringAsync任务完成。完成后,await会返回任务的结果并继续执行方法的其余部分。

异步编程的优势

  1. 提高响应性 异步方法不会阻塞主线程,使得应用程序在处理长时间运行的任务时仍然保持响应。

  2. 简化代码 使用asyncawait关键字可以避免回调地狱,使异步代码更加直观和易于维护。

3.解释一下什么是LINQ,并举例说明如何在C#中使用LINQ查询数据。

什么是LINQ?

LINQ是C#语言中的一种查询技术,它允许开发人员使用类似SQL的语法来查询各种数据源,例如集合、数组、数据库等。LINQ使得数据的查询和操作变得更加直观和简单,并且能够在编译时进行类型检查。

在C#中使用LINQ查询数据的基本步骤

  1. 引入命名空间

    首先,确保在文件开头引入System.Linq命名空间,因为LINQ扩展方法都在这个命名空间中定义。

    using System.Ling
  2. LINQ查询语法

    LINQ查询可以使用两种语法:查询表达式和方法语法。下面我们以方法语法为例进行说明。

    using System;
    using System.Linq;
    
    public class Program
    {
        public static void Main()
        {
            // 示例数据源 - 整数数组
            int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    
            // LINQ查询语句
            var query = numbers.Where(n => n % 2 == 0).Select(n => n * n);
    
            // 执行查询并输出结果
            foreach (var result in query)
            {
                Console.WriteLine(result);
            }
        }
    }
    

解释

  • Where 方法

    Where方法用于过滤数据源,筛选出符合特定条件的元素。在示例中,我们使用Where(n => n % 2 == 0)选择所有偶数。

  • Select 方法

    Select方法用于对数据源中的每个元素应用一个转换函数,并返回结果序列。在示例中,我们使用Select(n => n * n)计算每个偶数的平方。

  • 执行查询

    query是一个延迟执行的查询,只有在foreach循环中迭代时才会真正执行查询并输出结果。

LINQ的优势

  1. 统一的查询语法 LINQ提供了一种统一的语法来查询不同类型的数据源,无论是集合、数组、数据库还是XML。

  2. 类型安全和编译时检查 LINQ查询是类型安全的,并且能够在编译时检查语法错误,这减少了运行时错误的可能性。

  3. 简化代码 LINQ使得复杂的数据操作和查询变得简单和可读,减少了样板代码和嵌套结构。

4.解释一下在C#中什么是垃圾回收(Garbage Collection),它是如何工作的?

什么是垃圾回收(Garbage Collection)?

垃圾回收(GC)是一种自动内存管理机制,用于回收程序中不再使用的内存。它可以防止内存泄漏,确保程序的内存使用效率。C#中垃圾回收由CLR(Common Language Runtime)负责管理。

垃圾回收的工作原理

垃圾回收的主要任务是识别和释放那些不再被引用的对象所占用的内存。以下是垃圾回收的工作流程:

  1. 根集合(Root Set) 垃圾回收器首先找到所有活动的对象引用,这些引用通常存储在栈、全局对象、静态对象等地方。这些引用被称为根集合。

  2. 标记阶段(Marking Phase) 垃圾回收器从根集合开始,递归地标记所有可达的对象。被标记的对象被认为是活动的,不能被回收。

  3. 清除阶段(Sweeping Phase) 垃圾回收器遍历内存中的所有对象,清除那些未被标记的对象,释放它们占用的内存。

  4. 压缩阶段(Compacting Phase, 可选) 在某些情况下,垃圾回收器会对内存进行压缩,将存活的对象移动到内存的一端,以减少内存碎片并优化内存使用效率。

垃圾回收的代(Generation)

为了提高性能,C#的垃圾回收器使用了分代回收策略。内存中的对象被分为三代:

  1. 第0代(Generation 0) 包含新创建的对象。因为大多数对象的生命周期很短,垃圾回收器会频繁地对第0代进行回收。

  2. 第1代(Generation 1) 包含那些已经存活过一次第0代回收的对象。第1代回收相对较少。

  3. 第2代(Generation 2) 包含长生命周期的对象。第2代回收最不频繁,但会进行全面回收。

示例

using System;

public class Program
{
    public static void Main()
    {
        // 创建一些对象
        for (int i = 0; i < 1000; i++)
        {
            var obj = new object();
        }

        // 显示垃圾回收前的内存使用情况
        Console.WriteLine("GC前的内存使用: " + GC.GetTotalMemory(false));
        Console.ReadLine();

        // 强制进行垃圾回收
        GC.Collect();

        // 显示垃圾回收后的内存使用情况
        Console.WriteLine("GC后的内存使用: " + GC.GetTotalMemory(true));
        Console.ReadLine();
    }
}

 

using System;

class Program
{
    static void Main()
    {
        // 创建对象
        MyClass obj = new MyClass();
        
        // 使用对象
        obj.DoSomething();
        
        // 将对象设置为null,表示不再需要它
        obj = null;
        
        // 手动调用GC(通常不需要)
        GC.Collect();
        
        Console.WriteLine("垃圾回收完成");
    }
}

class MyClass
{
    public void DoSomething()
    {
        Console.WriteLine("Doing something");
    }
}

在这个示例中,我们创建了一些对象,然后使用GC.Collect()方法手动触发垃圾回收,并显示垃圾回收前后的内存使用情况。

垃圾回收的优势

  1. 自动内存管理 程序员不需要手动管理内存,减少了内存泄漏和悬挂指针的风险。

  2. 提高程序的稳定性和安全性 自动内存管理减少了内存管理错误,提高了程序的稳定性和安全性。

5.解释一下C#中的接口(Interface)是什么,以及如何在代码中使用接口。

在C#中,接口(Interface)是一种定义了一组无实现方法的类型。接口只包含方法、属性、事件或索引器的声明,而不包含任何实现。接口用于定义类或结构必须遵循的一组规则。它可以被实现(implement)在一个或多个类或结构中,以确保这些类或结构遵循接口中定义的规则。

示例:

// 定义一个接口
public interface IAnimal
{
    void MakeSound();
    void Eat();
}

// 实现接口的类
public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
        Console.ReadLine();
    }

    public void Eat()
    {
        Console.WriteLine("Dog is eating.");
        Console.ReadLine();
    }
}

public class Cat : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Meow");
        Console.ReadLine();
    }

    public void Eat()
    {
        Console.WriteLine("Cat is eating.");
        Console.ReadLine();
    }
}

 在这个例子中,IAnimal接口定义了两个方法:MakeSoundEatDogCat类实现了这个接口,并提供了各自的具体实现。

6.在C#中,抽象类和接口有什么区别?

  1. 实现方式

    • 接口:只能包含方法、属性、事件和索引器的声明,不能包含任何实现。
    • 抽象类:可以包含方法、属性的声明和实现。抽象方法在子类中必须实现,而具体方法可以直接使用或在子类中重写。
  2. 多重继承

    • 接口:一个类可以实现多个接口,这提供了一种方式来实现多重继承。
    • 抽象类:一个类只能继承一个抽象类(或任何其他类),不能多重继承。
  3. 构造函数

    • 接口:不能包含构造函数。
    • 抽象类:可以包含构造函数,供子类调用。
  4. 字段

    • 接口:不能包含字段。
    • 抽象类:可以包含字段。
  5. 访问修饰符

    • 接口:成员默认是public,不能包含其他访问修饰符。
    • 抽象类:可以包含不同访问修饰符(publicprotectedprivate)。

下面是一个例子来说明抽象类和接口的区别:

// 定义一个接口
public interface IAnimal
{
    void MakeSound();
}

// 定义一个抽象类
public abstract class Animal
{
    public abstract void MakeSound();
    
    public void Eat()
    {
        Console.WriteLine("Animal is eating.");
    }
}

// 实现接口的类
public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

// 继承抽象类的类
public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Meow");
    }
}

在这个例子中,IAnimal是一个接口,Animal是一个抽象类。Dog类实现了接口,Cat类继承了抽象类并实现了抽象方法。

7.在C#中,什么是泛型(Generics),以及为什么使用泛型?

在C#中,泛型(Generics)是一种允许你编写可以重用的类型或方法的技术。泛型允许在定义类、结构、接口和方法时使用类型参数,这些类型参数可以在使用时指定具体的类型。使用泛型可以提高代码的灵活性、类型安全性和性能。

泛型的作用和使用方法

  1. 作用

    • 允许编写通用的代码,可以适用于多种数据类型,而不需要重复编写相似的代码。
    • 提高了代码的类型安全性,编译器可以在编译时捕获到类型不匹配的错误。
    • 避免了装箱和拆箱操作,提高了性能。
  2. 使用方法

    • 泛型类:使用<T>语法定义泛型类,其中T是类型参数的占位符。
    • 泛型方法:使用<T>语法定义泛型方法,在方法名之后指定类型参数。
    • 泛型接口:定义泛型接口时,可以在接口名后面使用<T>语法指定类型参数。
using System;

// 定义一个泛型类
public class GenericList<T>
{
    private T[] data;
    private int index;

    public GenericList(int size)
    {
        data = new T[size];
        index = 0;
    }

    public void Add(T item)
    {
        if (index < data.Length)
        {
            data[index] = item;
            index++;
        }
        else
        {
            Console.WriteLine("List is full.");
        }
    }

    public void Print()
    {
        foreach (T item in data)
        {
            Console.WriteLine(item);
        }
    }
}

class Program
{
    static void Main()
    {
        // 实例化一个泛型类
        GenericList<int> intList = new GenericList<int>(5);
        intList.Add(1);
        intList.Add(2);
        intList.Add(3);
        intList.Print();

        GenericList<string> stringList = new GenericList<string>(3);
        stringList.Add("Hello");
        stringList.Add("World");
        stringList.Print();
    }
}

 

在这个例子中,GenericList<T>是一个泛型类,可以用来存储任何类型的数据。在Main方法中,我们实例化了两个泛型类的对象,一个存储整数,一个存储字符串,并演示了如何向泛型类中添加元素并打印它们。

泛型在C#中是一个非常强大和常用的特性,可以大大简化和提高代码的复用性和可维护性。

8.在C#中,什么是异常处理(Exception Handling),如何使用try-catch-finally块来处理异常?

在C#中,异常处理是一种机制,用于捕获和处理程序运行时发生的异常情况,例如除以零、空引用等。异常处理允许程序员在代码中定义如何处理这些异常,从而使程序能够在遇到问题时进行优雅的处理,而不是直接崩溃或产生不可预期的结果。

异常处理的基本方法

异常处理通常使用try-catch-finally块来实现:

  1. try:在try块中编写可能会抛出异常的代码。

  2. catch:在catch块中指定希望捕获的异常类型,并定义异常处理逻辑。

  3. finally:在finally块中编写无论是否发生异常都需要执行的代码,例如资源释放。

以下是一个简单的示例,展示如何使用try-catch-finally块处理异常:

using System;

class Program
{
    static void Main()
    {
        try
        {
            // 可能会抛出异常的代码
            int a = 10;
            int b = 0;
            int result = a / b; // 除以零会抛出 DivideByZeroException
            Console.WriteLine($"Result: {result}"); // 不会执行到这里
        }
        catch (DivideByZeroException ex)
        {
            // 捕获特定类型的异常,并处理
            Console.WriteLine("Error: Divide by zero");
        }
        catch (Exception ex)
        {
            // 捕获所有其他类型的异常
            Console.WriteLine($"An error occurred: {ex.Message}");
        }
        finally
        {
            // 无论是否发生异常,最终都会执行的代码
            Console.WriteLine("Finally block executed.");
        }
    }
}

 

在这个例子中:

  • try块包含了除以零的代码,可能会抛出DivideByZeroException异常。
  • catch块用于捕获DivideByZeroException异常,并输出错误信息。
  • finally块包含了无论是否发生异常都会执行的代码。

catch块可以指定多个,每个catch块可以捕获不同类型的异常,或者可以使用一个通用的catch (Exception ex)捕获所有未处理的异常。finally块是可选的,用于确保资源的释放或其他清理操作。

异常处理使得程序在面对问题时能够做出合适的响应,从而提高了程序的稳定性和可靠性。

9.在C#中,什么是委托链(Delegate Chain),它的作用是什么?

在C#中,委托链(Delegate Chain)是多个委托的组合,这些委托可以依次被调用。委托链允许你在一个委托实例中包含多个方法,当调用这个委托实例时,链中的所有方法都会按顺序执行。

委托链的作用

  • 多播委托:委托链可以看作是多播委托(Multicast Delegate),它允许一个委托实例引用多个方法。
  • 事件处理:在事件处理机制中,委托链用于将多个事件处理程序连接在一起,当事件被触发时,所有处理程序都会被调用。

创建和使用委托链

  1. 定义委托:定义一个委托类型。
  2. 实例化和组合委托:创建委托实例,并使用++=操作符将多个方法添加到委托链中。
  3. 调用委托链:调用委托实例,依次执行链中的所有方法。

以下是一个简单的示例,展示如何创建和使用委托链:

using System;

public delegate void MyDelegate(string message);

class Program
{
    static void Main()
    {
        // 创建委托实例并将方法添加到委托链中
        MyDelegate del1 = Method1;
        MyDelegate del2 = Method2;
        MyDelegate delChain = del1 + del2;

        // 也可以使用 += 操作符
        // MyDelegate delChain = del1;
        // delChain += del2;

        // 调用委托链
        delChain("Hello, world!");
    }

    static void Method1(string message)
    {
        Console.WriteLine("Method1 received: " + message);
    }

    static void Method2(string message)
    {
        Console.WriteLine("Method2 received: " + message);
    }
}

 

在这个例子中:

  • 定义了一个MyDelegate委托类型,它接受一个字符串参数并返回void
  • Method1Method2是两个与MyDelegate匹配的方法。
  • 使用+操作符将del1del2组合成一个委托链delChain
  • 调用delChain时,Method1Method2会依次被调用,输出传入的消息。

运行结果:

Method1 received: Hello, world!
Method2 received: Hello, world!

委托链在事件处理和回调机制中非常有用,可以将多个处理程序连接在一起,从而实现复杂的处理逻辑。

10.在C#中,什么是事件(Event),以及如何使用事件?

在C#中,事件(Event)是一种特殊的委托,用于实现发布-订阅模式。事件允许对象向外部通知某些动作或状态的变化,而不需要让外部对象知道内部的具体实现细节。事件在GUI编程和异步编程中非常常用。

事件的基本概念和使用方法

  1. 定义事件:事件基于委托类型定义。使用event关键字来声明事件。

  2. 触发事件:在类的内部方法中,通过调用事件委托来触发事件。

  3. 订阅事件:外部对象可以通过+=操作符订阅事件,指定事件触发时要调用的方法。

事件的示例

以下是一个简单的示例,展示如何定义、订阅和触发事件:

using System;

// 定义一个委托类型,用于事件处理
public delegate void MyEventHandler(string message);

// 发布者类
public class Publisher
{
    // 使用event关键字定义事件
    public event MyEventHandler MyEvent;

    // 触发事件的方法
    public void RaiseEvent(string message)
    {
        // 触发事件,检查是否有订阅者
        MyEvent?.Invoke(message);
    }
}

// 订阅者类
public class Subscriber
{
    // 事件处理方法
    public void OnEventRaised(string message)
    {
        Console.WriteLine("Event received: " + message);
    }
}

class Program
{
    static void Main()
    {
        Publisher publisher = new Publisher();
        Subscriber subscriber = new Subscriber();

        // 订阅事件
        publisher.MyEvent += subscriber.OnEventRaised;

        // 触发事件
        publisher.RaiseEvent("Hello, world!");
    }
}

 

在这个例子中:

  1. 定义事件

    • MyEventHandler是一个委托类型,定义了事件处理方法的签名。
    • Publisher类中,使用event关键字声明了一个事件MyEvent
  2. 触发事件

    • Publisher类中,RaiseEvent方法触发事件,使用?.Invoke来检查是否有订阅者。
  3. 订阅事件

    • Program类中,创建了PublisherSubscriber对象。
    • 使用+=操作符将Subscriber类的OnEventRaised方法订阅到Publisher类的MyEvent事件。

运行结果:

Event received: Hello, world!

事件机制使得发布者和订阅者之间的耦合度降低,提供了一种松散耦合的方式来处理异步通知和回调。

11.在C#中,什么是属性(Property),以及如何使用自动属性?

在C#中,属性(Property)是类、结构和接口中的成员,它提供了对字段的访问控制。属性看起来像是字段,但实际上是方法的特殊组合,用于读取和写入字段的值。属性可以包含逻辑,用于在设置或获取值时执行特定操作。

属性的基本概念和使用方法

  1. 定义属性:属性由getset访问器组成,可以在访问器中定义读取或写入字段时的逻辑。

  2. 自动属性:自动属性是一种简化的语法,它不需要显式定义字段,编译器会自动创建一个私有的匿名字段来存储属性的值。

属性的示例

以下是一个简单的示例,展示如何定义和使用属性:

using System;

public class Person
{
    private string name;
    
    // 定义一个属性
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    // 使用自动属性
    public int Age { get; set; }

    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

class Program
{
    static void Main()
    {
        Person person = new Person();

        // 使用属性
        person.Name = "John";
        person.Age = 30;

        person.DisplayInfo(); // 输出:Name: John, Age: 30
    }
}

 

在这个例子中:

  1. 定义属性

    • Name属性有一个私有字段nameget访问器用于返回name的值,set访问器用于设置name的值。
  2. 自动属性

    • Age属性使用了自动属性语法,编译器会自动生成一个私有的匿名字段来存储Age的值。这样可以简化代码,使其更简洁。

属性提供了对类成员的封装,使得可以在设置或获取值时添加额外的逻辑,从而增强了代码的灵活性和安全性。

属性的常见用法

  1. 只读属性:只包含get访问器的属性,不能修改值。
  2. 写入属性:只包含set访问器的属性,只能设置值,不能读取。
  3. 计算属性:在getset访问器中包含逻辑,用于计算或验证值。
示例:只读属性
public class Circle
{
    private double radius;

    public Circle(double radius)
    {
        this.radius = radius;
    }

    // 只读属性
    public double Circumference
    {
        get { return 2 * Math.PI * radius; }
    }
}

在这个示例中,Circumference属性是一个只读属性,它在get访问器中计算圆的周长。

12.在C#中,什么是匿名方法和Lambda表达式?

在C#中,匿名方法和Lambda表达式是两种用于定义无名称方法的技术。它们主要用于简化代码,特别是在使用委托和事件时。

匿名方法

匿名方法是没有名称的方法,可以在代码中直接内联定义。使用delegate关键字来定义匿名方法。

示例:匿名方法
using System;

class Program
{
    // 定义一个委托类型
    public delegate void MyDelegate(string message);

    static void Main()
    {
        // 使用匿名方法创建委托实例
        MyDelegate del = delegate (string message)
        {
            Console.WriteLine("Anonymous method: " + message);
        };

        // 调用委托
        del("Hello, world!");
    }
}

 

在这个示例中,匿名方法通过delegate关键字定义,并传递给委托del。然后,调用委托时执行匿名方法。

Lambda表达式

Lambda表达式是一种更简洁的匿名方法语法。它使用=>符号来分隔参数和方法体。

示例:Lambda表达式
using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // 使用Lambda表达式创建委托实例
        Func<int, int, int> add = (a, b) => a + b;
        
        // 调用委托
        int result = add(2, 3);
        Console.WriteLine("Lambda expression result: " + result);
        
        // 另一个示例:使用Lambda表达式查询集合
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
        var evenNumbers = numbers.Where(n => n % 2 == 0);

        foreach (var number in evenNumbers)
        {
            Console.WriteLine("Even number: " + number);
        }
    }
}

 

在这个示例中:

  • Func<int, int, int>是一个内置委托类型,表示接受两个int参数并返回一个int结果的委托。
  • Lambda表达式(a, b) => a + b定义了一个方法体,计算并返回两个整数的和。
  • 使用Lambda表达式n => n % 2 == 0在集合numbers中查询偶数。

匿名方法和Lambda表达式的比较

  • Lambda表达式语法更加简洁和直观,特别是用于简单的操作。
  • 匿名方法在需要复杂逻辑或多行代码时更具可读性。

使用场景

  • 事件处理:简化事件处理程序的定义。
  • LINQ查询:用于编写简洁的查询表达式。
  • 回调:在异步编程和任务中定义回调方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1913011.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【无聊找点事干】局域网内把window 搭建为代理服务器上网

场景描述 同一局域网内&#xff0c;server 2012可以上网&#xff0c;window 10无法上网。现在将电脑server 2012设置为代理服务器&#xff0c;使得window 10可以通过server 2012访问公网。 server 2012&#xff1a;服务端 安装代理服务器软件&#xff1a;CCProxy点击‘设置’…

OAuth2.0登录的四种方式

OAuth登录的四种方式 1. 授权码 授权码&#xff08;authorization code&#xff09;方式&#xff0c;指的是第三方应用先申请一个授权码&#xff0c;然后再用该码获取令牌。 这种方式是最常用的流程&#xff0c;安全性也最高&#xff0c;它适用于那些有后端的 Web 应用。授权…

C语言 | Leetcode C语言题解之第226题翻转二叉树

题目&#xff1a; 题解&#xff1a; struct TreeNode* invertTree(struct TreeNode* root) {if (root NULL) {return NULL;}struct TreeNode* left invertTree(root->left);struct TreeNode* right invertTree(root->right);root->left right;root->right le…

全网最适合入门的面向对象编程教程:12 类和对象的 Python 实现-Python 使用 logging 模块输出程序运行日志

全网最适合入门的面向对象编程教程&#xff1a;12 类和对象的 Python 实现-Python 使用 logging 模块输出程序运行日志 摘要&#xff1a; 本文主要介绍了日志的定义和作用&#xff0c;以及 Python 内置日志处理的 logging 模块&#xff0c;同时简单说明了日志等级和 logging …

Redis 7.x 系列【21】主从复制

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 工作原理2.1 建立连接2.2 全量复制2.3 命令传播2.4 增量复制 3. 拓扑架构3.…

Git分支结构

目录 1. 线性分支结构 2. 分叉与合并结构 3. 分支与标签的关系 4. 并行开发与分支管理策略 测试&#xff08;本机系统为Rocky_linux9.4&#xff09; 合并失败解决 删除分支 删除本地分支 删除远程分支 Git 中的分支结构是版本控制中非常重要的概念之一&#xff0c;它描…

Golang | Leetcode Golang题解之第225题用队列实现栈

题目&#xff1a; 题解&#xff1a; type MyStack struct {queue []int }/** Initialize your data structure here. */ func Constructor() (s MyStack) {return }/** Push element x onto stack. */ func (s *MyStack) Push(x int) {n : len(s.queue)s.queue append(s.queu…

【学术会议征稿】第三届人工智能与智能信息处理国际学术会议(AIIIP 2024)

第三届人工智能与智能信息处理国际学术会议&#xff08;AIIIP 2024&#xff09; 2024 3rd International Conference on Artificial Intelligence and Intelligent Information Processing 第三届人工智能与智能信息处理国际学术会议&#xff08;AIIIP 2024&#xff09;将于…

初始化线程的4种方式

1. 继承Thread 缺点&#xff1a;无法获取线程的运算结果。 public class ThreadTest{public static void main(String[] args){Thread01 thread new Thread01();thread.start();}public static class Thread01 extends Thread{public void run(){System.out.println("当前…

Qt(一)概念 信号与槽

文章目录 一、概念&#xff08;一&#xff09;Qt工具1. Assistant&#xff1a;帮助手册2. Designer&#xff1a;Qt设计师3. xxx.uic文件4. rcc资源文件5. moc&#xff1a;元对象编译器6. qmake7. Qtcreator&#xff1a;集成化的开发软件 &#xff08;二&#xff09;创建第一个Q…

C 语言中的联合(Union)的用途是什么?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; &#x1f4d9;C 语言百万年薪修炼课程 通俗易懂&#xff0c;深入浅出&#xff0c;匠心打磨&#xff0c;死磕细节&#xff0c;6年迭代&#xff0c;看过的人都说好。 文章目…

飞速(FS)10G光模块选择指南

飞速&#xff08;FS&#xff09;的10G SFP光模块专为万兆每秒&#xff08;10 Gbps&#xff09;的数据传输设计&#xff0c;满足多样化网络需求。该光模块支持多种传输距离&#xff0c;具备热插拔和数字诊断监控功能&#xff0c;全面适配200品牌&#xff0c;为客户提供更灵活的选…

智慧灌区信息化系统完整解决方案

一、背景 随着科技的快速发展&#xff0c;智慧灌区信息化系统正逐渐成为提高农业灌溉效率、优化水资源配置的重要手段。本文将详细介绍智慧灌区信息化系统的完整解决方案&#xff0c;包括系统、功能、应用以及优势分析等方面&#xff0c;旨在为灌区的现代化和高效管理提供有力…

clickhouse-jdbc-bridge rce

clickhouse-jdbc-bridge 是什么 JDBC bridge for ClickHouse. It acts as a stateless proxy passing queries from ClickHouse to external datasources. With this extension, you can run distributed query on ClickHouse across multiple datasources in real time, whic…

爆破器材期刊

《爆破器材》简介   《爆破器材》自1958年创刊以来&#xff0c;深受广大读者喜爱&#xff0c;是中国兵工学会主办的中央级技术刊物&#xff0c;在国内外公开发行&#xff0c;近几年已发行到10个国家和地区。《爆破器材》杂志被美国著名检索机构《化学文摘》&#xff08;CA&a…

Spin Image(旋转图像)

Spin Image特征描述子原理 Spin Image是Johnson于1999年提出&#xff0c;Lazebnik于2005年完善的基于点云空间分布的特征描述方法&#xff0c;其思想是将一定区域的点云分布转换成二维的Spin Image&#xff0c;然后对场景和模型的Spin Image进行相似性度量。Spin Image方法与通…

数据结构--二叉树收尾

1.二叉树销毁 运用递归方法 分类&#xff1a; 根节点左子树右子树&#xff08;一般都是这个思路&#xff0c;不断进行递归即可&#xff09; 选择方法&#xff08;分析)&#xff1a; 前序&#xff1a;如果直接销毁根就无法找到左子树右子树 中序&#xff1a;也会导致丢失其…

Linux|背景 环境搭建

目录 一、简述Linux发展史 1.1计算机的诞生 1.2操作系统的诞生 1.3Linux操作系统开源 1.4Linux发行版本 二、搭建Linux环境 三、使用shell远程登入到Linux 一、简述Linux发展史 可能大家未听说过Linux&#xff0c;或者只知道它是一个搭配在计算机上的操作系统&#xff0…

详细谈谈负载均衡的startupProbe探针、livenessProbe探针、readnessProbe探针如何使用以及使用差异化

文章目录 startupProbe探针startupProbe说明示例配置参数解释 使用场景说明实例——要求&#xff1a; 容器在8秒内完成启动&#xff0c;否则杀死对应容器工作流程说明timeoutSeconds: 和 periodSeconds: 参数顺序说明 livenessProbe探针livenessProbe说明示例配置参数解释 使用…

OpenCV MEI相机模型(全向模型)

文章目录 一、简介二、实现代码三、实现效果参考文献一、简介 对于针孔相机模型,由于硬件上的限制(如进光量等),他的视野夹角往往有效区域只有140度左右,因此就有研究人员为每个针孔相机前面再添加一个镜片,如下所示: 通过折射的方式增加了相机成像的视野,虽然仍然达不…