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

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查询:用于编写简洁的查询表达式。
  • 回调:在异步编程和任务中定义回调方法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hive表【汇总】

提前必备 1、内部表和外部表的区别 概念讲解&#xff1a; 外部表&#xff1a;1、存放他人给予自己的数据2、当我们删除表操作时&#xff0c;会将表的元数据删除&#xff0c;保留数据文件 内部表&#xff1a;1、存放已有的数据2、当我们删除表操作时&#xff0c;会将表的元数据…

Unity 优化合集

1️⃣ 贴图优化 1. Read/Write Enable 这个属性勾选后允许你在运行时读取和写入纹理数据&#xff0c;这对于需要实时生成内容或者需要动态修改纹理的场合非常有用但在大部分情况下这是不必要的。如果打开这个属性&#xff0c;会使运行时贴图大小翻倍&#xff0c;内存中会额外…

缓存与分布式锁

一、缓存 1、缓存使用 为了系统性能的提升&#xff0c;我们一般都会将部分数据放入缓存中&#xff0c;加速访问。 适合放入缓存的数据有&#xff1a; 即时性、数据一致性要求不高的&#xff1b;访问量大且更新频率不高的数据。 在开发中&#xff0c;凡是放入缓存中的数据我们都…

Git 命令行快速入门

前言 &#xff08;1&#xff09;新手个人建议使用TortoiseGit这类图形化界面来上手学习。 &#xff08;2&#xff09;如果一定需要用命令行进行操作&#xff0c;可以按照B站&#xff1a;程式与网页开发者必备技能&#xff01;Git 和 GitHub 零基础快速上手&#xff0c;轻松掌握…

构造与操作链栈

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍+ 收藏⭐ + 留言​📝心态决定高度,细节决定成败! 链栈是数据结构中栈的一种实现方式,它利用链表(通常是单链表)来存储栈中的元…

【触摸屏】【红十字会学习系统】功能模块:视频 + AI拍照合成

项目背景 提升公众急救能力&#xff1a;确保每个人都能在紧急情况下采取正确的急救措施&#xff0c;减少伤害&#xff0c;挽救生命。培养人道主义价值观&#xff1a;通过教育和培训&#xff0c;传播红十字精神&#xff0c;促进社会对弱势群体的关注与支持。建立社区响应网络&a…

1. InternLM - 入门岛

第1关 Linux 基础知识 1. 完成SSH连接与端口映射并运行hello_world.py SSH连接配置 # wsl2中生成密钥对&#xff08;~/.ssh/id_rsa, ~/.ssh/id_rsa.pub&#xff09; ssh-keygen -t rsa# 将id_rsa.pub在internStudio作为公钥导入SSH登录 $ ssh -p 38871 rootssh.intern-ai.o…

5.SpringBoot核心源码-启动类源码分析

目录 概述技巧spring boot 如何启动应用程序run方法里面核心逻辑 SpringApplicaiton.run(xxx.class,args)结束 概述 SpringBoot核心源码-启动类源码分析 技巧 如何给外部源码加注释&#xff0c;想要在源码中添加自己的注释&#xff0c;会弹出 file is read only&#xff0c;代…

Java核心技术【二十二】Java的I/O流处理:深入文件读写操作、缓冲流、序列化与NIO

Java的I/O流处理&#xff1a;深入文件读写操作、缓冲流、序列化 在Java编程中&#xff0c;I/O流是处理输入输出操作的基础&#xff0c;特别是在文件读写、网络通信等领域。本文将在前文的基础上&#xff0c;进一步探讨缓冲流、序列化以及NIO&#xff08;New I/O&#xff09;在…

从0开始的STM32HAL库学习2

外部中断(HAL库GPIO讲解) 今天我们会详细地学习STM32CubeMX配置外部中断&#xff0c;并且讲解HAL库的GPIO的各种函数。 准备工作&#xff1a; 1、STM32开发板&#xff08;我的是STM32F103C8T6&#xff09; 2、STM32CubeMx软件、 IDE&#xff1a; Keil软件 3、STM32F1xx/ST…

01- 收入数据集【Pytorch入门实战】

目录 一、机器学习基础 二、实战例子 1.数据集分析 2.实战训练 3.总结 三、参考资料 一、机器学习基础 为了解决这个问题&#xff0c;人们想到数据驱动方法&#xff0c;也就是让计算机从现有的大量的带标签图片电学习规律&#xff0c;一旦计算机学习到了其中的规律&…

sip协议栈简介

SIP协议栈简介 SIP协议栈流程 数据链路层&#xff1a;当SIP消息从网络中传输到达TCP/IP协议栈时&#xff0c;首先被接收到的是数据链路层的数据帧。数据链路层会对数据帧进行解封装&#xff0c;得到网络层的IP数据报。 网络层&#xff1a;网络层会对IP数据报进行解析&#xf…

1.27、基于径向基神经网络的曲线拟合(matlab)

1、基于径向基神经网络的曲线拟合简介及原理 1)原理简介 基于径向基神经网络(Radial Basis Function Neural Network, RBFNN)的曲线拟合是一种常用的非线性拟合方法,通过在输入空间中使用径向基函数对数据进行处理,实现对非线性关系的拟合。 RBFNN的基本原理是将输入空…

Java基础(十九):集合框架

目录 一、Java集合框架体系二、Collection接口及方法1、添加2、判断3、删除4、其它 三、Iterator(迭代器)接口1、Iterator接口2、迭代器的执行原理3、foreach循环 四、Collection子接口1&#xff1a;List1、List接口特点2、List接口方法3、List接口主要实现类&#xff1a;Array…

【Hive SQL 每日一题】在线峰值人数计算

文章目录 测试数据需求说明需求实现 测试数据 -- 创建 user_activity 表 DROP TABLE IF EXISTS user_activity ; CREATE TABLE user_activity (user_id STRING,activity_start TIMESTAMP,activity_end TIMESTAMP );-- 插入数据 INSERT INTO user_activity VALUES (user1, 2024…

算效最高的智算中心上线,天府智算为AI产业带来哪些启示?

四川简阳&#xff0c;地处川中、控扼巴峡&#xff0c;自古乃成渝、川鄂之间的交通重镇&#xff0c;素有“天府雄州”之美誉。 步入数字经济时代&#xff0c;“天府雄州”得天独厚的地理位置再次彰显出巨大的战略价值。简阳市成为成渝算力枢纽的天府数据中心集群关键布局点&…

element ui ts table重置排序

#日常# 今天带的实习生&#xff0c;在遇到开发过程中&#xff0c;遇到了element ui table 每次查询的时候都需要重置排序方式&#xff0c;而且多个排序是由前端排序。 <el-table :data"tableData" ref"restTable"> </<el-table> <script…

bi项目笔记

1.bi是什么 bi项目就是商业智能系统&#xff0c;也就是数据可视画、报表可视化系统&#xff0c;如下图的就是bi项目了 2.技术栈

深入了解 MySQL 的 EXPLAIN 命令

一、什么是 EXPLAIN 命令&#xff1f; EXPLAIN 命令用于显示 MySQL 如何执行某个 SQL 语句&#xff0c;尤其是 SELECT 语句。通过 EXPLAIN 命令&#xff0c;可以看到查询在实际执行前的执行计划&#xff0c;这对于优化查询性能至关重要。 二、EXPLAIN 的基本用法 要使用 EXP…

什么时候要用弗洛伊德算法

分析一下题目&#xff0c;我们看到数据量只有一百&#xff0c;这个时候我们就要注意是否是要用弗洛伊德算法&#xff0c;然后接着我们还需要枚举每一种情况&#xff0c;我们可以用到next_permutation这个方法 #include<bits/stdc.h> using namespace std;const int N 10…