65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET 重要主题总结 - 代码示例

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.37/5 (66投票s)

2015年3月20日

CPOL

7分钟阅读

viewsIcon

118604

.NET 访谈问题及答案,包含真实世界示例和代码片段

什么是委托?

委托封装方法,将其视为一流对象。声明委托时,后端会创建一个类。委托可以被添加/删除,称为委托链或多播委托。通过委托,您可以将方法作为参数发送,例如

    public static class DeletageExample
    {
        private delegate void MyDelegate(string txt); //Declare Delegate

        public static void DeletageExampleMethod()
	    {
            MyDelegate del = SendText; //Create Delegate object and assign function SendText
            del("Display this"); //Invoke Delegate function
        }

        public static void SendText(string txt)
        {
            Console.WriteLine(txt);
        }
    }

//Delegate Chaining or Multicast Delegate

public static class DeletageExample
    {
       private delegate void MyDelegate(string txt);

       public static void DeletageExampleMethod()

        {
            MyDelegate txt1 = SendText;
            MyDelegate txt2 = SendText2;
            txt1 += txt2;
            txt1("hay u");
        }

        public static void SendText(string txt)
        {
            Console.WriteLine(txt);
        }

        public static void SendText2(string txt)
        {
            Console.WriteLine(txt+" I am second delegate");
        }

    }

什么是匿名方法?

没有名称的方法,匿名方法用于缩短委托代码。与其创建一个方法并将其分配给委托,不如在委托赋值语句中编写方法体。只为简短方法使用匿名方法作为替代方案,例如

    private delegate void MyDelegate(string txt);

     public static void DeletageExampleMethod()
        {
           MyDelegate txt1 = delegate(string txt) { Console.WriteLine(txt); };
           txt1("I am Anonymous method");
        }

什么是 Lambda 表达式?

Lambda 表达式用于缩短匿名方法,Lambda 运算符 (=>) 用于替换 delegate 关键字。例如

private delegate void MyDelegate(string txt);

 public static void DeletageExampleMethod()
     {
         MyDelegate txt1 =  txt => { Console.WriteLine(txt); }; //Use () for more than 
								//one input parameters
         txt1("I am Anonymous method");
     }

什么是泛型委托?

为了使委托代码更短并消除委托声明,可以使用 Action<>Func<> 泛型委托。

  • Action<>:无返回值,最多可接受 16 个输入参数
  • Func<>:有返回值,最多可接受 16 个输入参数和一个返回值
  public static void DeletageExampleMethod()
      {
          Action<string> txt1 = txt => Console.WriteLine(txt);
          txt1("I don't have output ");
          Func<string, string> txt2 = txt =>
            {
               Console.WriteLine(txt);
               return "I am returning Value";
            };
       }

什么是事件?

事件是一种以订阅/监听者模式工作的委托。换句话说,事件和委托携手合作。以下是一个非常基本的事件示例,关于一个人从银行提款。对于任何类型的账户,都应从余额中扣除款项。可以使用 Sender 对象和 EventArgs 来确定账户类型和请求发送者。为使示例简单化,仅实现了基本功能。

    class Program
    {
        static void Main(string[] args)
        {
            var account1 = new AccountManagement();
            var person = new Person(account1) { Name = "John Doe" };
            account1.Winthdraw(person);
            var account2 = new AccountManagement();
            var person2 = new Person(account2) { Name = "Justin Phillip" };
            account2.Winthdraw(person2);
            Console.ReadLine();
        }
    }

    public class Person
    {
        public string Name { get; set; }
        private AccountManagement _account;
        public Person( AccountManagement account)
        {
            _account = account;
            account.Deposithandler += account.DeductMoney; //Subscribe to Deduct Money Event
        }
    }   

    public  class AccountManagement
    {
        public delegate void DepostHandler(object sender, string eventArgs);
        public event DepostHandler Deposithandler;

        public void DeductMoney(object sender, string eventArgs)
        {
            Console.WriteLine(eventArgs + ": Money has been deducted from your account\n");
        }

        public void Winthdraw(Person person)
        {
            Console.WriteLine(person.Name+": You withdraw money");
            Deposithandler(this, person.Name);
        }
    }

什么是隐式类型化或 var 关键字?

通过 var 关键字进行的隐式类型化在声明和初始化时确定变量的数据类型。Var 与某些语言中使用的 Variant 数据类型不同,仍然具有强类型特性。Var 变量不能初始化为 null ,因为编译器无法确定其数据类型(只有引用类型变量可以声明为 null )。您需要隐式地将 null 值转换为某种引用类型,例如 string

var accountnum = "342425756342"; //Correct, compiled as string
accountnum = 50;                 //Compiler error since it is considered as string in first statement.
var username = null;             //Compiler error, unable to determine the datatype
var username = (string)null;     //Successfully compile, string is reference.

什么是 Entity Framework 中的 Code-First 方法?

在 Code-First 方法中,您不必担心数据库设计/开发。首先创建应用程序设计,例如模型类/实体。完成后,您可以创建继承自 DbContext 的类,并创建模型类的 DBSet 。当您访问该 DbContext 类并执行某些 CRUD 操作时,数据库将根据您在 Context 构造函数中提供的名称自动创建。在本地的 SQLExpress 中查找该数据库。

    public class Student
    {
        public Student()
        {

        }

        public int StudentID { get; set; }

        public string StudentName { get; set; }
    }

    public class Standard
    {
        public Standard()
        {

        }

        public int StandardId { get; set; }

        public string StandardName { get; set; }

        public string Description { get; set; }
    }

    public class Context : DbContext
    {
        public Context(): base("StudentDB.Test") //Database Name
        {

        }

        public DbSet<Student> Students { get; set; }

        public DbSet<Standard> Standards { get; set; }
    }

   class Program
    {
        static void Main(string[] args)
        {
            using (var ctx = new Context())
            {
                var stud = new Student() { StudentName = "New Student" };
                ctx.Students.Add(stud);
                ctx.SaveChanges();
            }
        }
    }

什么是泛型?

一个简单的定义是将数据与其类型分开。在某些情况下,您可能需要在一种以上的数据类型中存储或获取数据。例如,您有一个 student 类,grade1 学生的 ID 只能是整数,而 grade2 学生的 ID 应该是 string 。这是一个 愚蠢的例子,但我是用它来概念验证的。为了有效地实现一个可以验证整数和 string ID 的类,您可能需要声明两个 student 类。但是,如果您想为一个班级(grade1 grade2 )的学生使用一个类,泛型将非常有帮助。

       static void Main(string[] args)
        {
            var Grad1Student = new Student<int> {StudentName = "John", StudentId = 1};
            var Grad2Student = new Student<string> { StudentName = "John", StudentId = "R001" };
        }

        public class Student<T>
        {
            public string  StudentName { get; set; }
            public T StudentId { get; set; }
        }

以下是一个示例,其中可以将泛型类的对象发送到方法进行进一步处理

     static void Main(string[] args)
        {
            var Grad1Student = new Student<int> {StudentName = "John", StudentId = 1};
            var Grad2Student = new Student<string> { StudentName = "John", StudentId = "R001" };
            var print = new Print();
            print.PrintStudents(Grad1Student);
            print.PrintStudents(Grad2Student);
            Console.ReadLine();
        }

        public class Student<T>
        {
            public string  StudentName { get; set; }
            public T StudentId { get; set; }
        }

        public class Print
        {
           public void PrintStudents<T>(Student<T> student)
            {
              Console.WriteLine(student.StudentId);
            }
        }

什么是 Reflection?

反射用于获取类、对象或程序集的元数据,例如程序集名称及其版本、程序集中所有类名的列表、类的属性/字段和方法等。您还可以获取属性/字段的值并调用方法。反射与特性(Attribute)可以帮助构建非常灵活的应用程序,您可以在运行时访问类、方法并调用它们。通过反射和特性,可以轻松导航第三方程序集。以下是一个简单的示例。

  static void Main(string[] args)
       {
         var assembies = Assembly.GetExecutingAssembly();
         Console.WriteLine("assembies are " + assembies.FullName);
         var classes = assembies.GetTypes();

            foreach (var classname in classes)
             {
                Console.WriteLine("Classes are: " + classname.FullName);
                
		 foreach (var prop in classname.GetProperties())
                  {
                    Console.WriteLine("\t"+ prop.Name);
                  }

                 foreach (var method in classname.GetMethods())
                  {
                    Console.WriteLine("\t" + method.Name);
                  }
            }

            var onlyattrubuteclass =

                assembies.GetTypes().Where(t => t.GetCustomAttributes<MyClassAttribute>().Any());

                foreach (var attrubteclass in onlyattrubuteclass)
                {
                  Console.WriteLine("\n\nAttributed Class " + attrubteclass.FullName);
                }

            Console.ReadLine();
        }

        [AttributeUsageAttribute(AttributeTargets.Class)]
        public class MyClassAttribute : Attribute
        {

        }

        [MyClass]
        public class MyTest
        {
            public string Name { get; set; }
            public int Id { get; set; }
            public void TestMethod()
            {

            }
        }

什么是扩展方法?

扩展方法顾名思义,用于扩展现有类的功能。这是一个 static 方法,其第一个参数是需要扩展的类的类型,并带有额外的关键字 "this"。扩展方法通过 (.) 运算符访问,就像 static 类方法一样,但需要类的对象。通常,扩展方法适用于我们没有第三方 DLL 代码的情况,但这方面的看法因人而异。以下是一个扩展方法的简单示例。

 class Program
    {
        static void Main(string[] args)
        {
            var test = new MyTest {Name = "John"};
            test.MyTestExtesnionMethod();
            Console.ReadLine();
        }
    }

    public class MyTest
    {
        public string Name { get; set; }
        public int Id { get; set; }
    }

    public static class ExtendMyTest
    {
        public static void MyTestExtesnionMethod(this MyTest test)
        {
            Console.WriteLine("I am extension method " + test.Name);
        }
    }

什么是 Dynamic 和 Late Binding?

Dynamic 是 .NET 中一种新的类型,用于在变量初始化时绕过编译器检查,并在运行时进行检查。它更灵活但也很危险,需要更谨慎地使用。dynamic 的一个很酷的功能是能够与其他语言的代码进行交互并执行它们。例如,在示例中,PythonIron 用于与 Python 代码交互并执行它。此外,dynamic 可以与 .NET 中可用的 ExpandoObject 类一起使用,在该类中您可以声明属性并在运行时使用它们。在 ASP.NET MVC 中,ViewBag 用于在 Controller 和 Views 之间传递数据,这是一个 dynamic 的良好示例。运行 Python 代码的简单示例是通过 dynamic 完成的:(从 NuGet 获取 IronPython)

 public static void Main()
        {
            var pythonRuntime = Python.CreateRuntime();
            dynamic pythonFile = pythonRuntime.UseFile("Test.py");
            pythonFile.SayHellotoPython();
            Console.ReadLine();
        }

其中 Python.py

import sys;
def SayHellotoPython();
    print "Hello I am Python"

下面给出了 ExpandoObject 的示例,它在 ASP.NET MVC 中就像 ViewBag 一样工作

 public static void Main()
      {
         dynamic exp = new ExpandoObject();
         exp.Name = "John";
         exp.Age = 56;
         Console.WriteLine(exp.Name);
         Console.WriteLine(exp.Age);
         Console.ReadLine();
      }

什么是可选参数?

如果您不知道或不想在每次调用方法时都为所有参数发送值,可选参数就很有用。它是方法重载的一个很好的替代方案,您为具有相同名称的不同参数集创建单独的方法。创建可选参数时有几点需要记住:必需参数应放在开头,并且参数的顺序非常重要。例如,如果您的方法有 3 个参数,第一个是必需的,后两个是可选的,而您不想指定第二个参数的值,您将收到编译器错误,因为编译器无法映射参数。解决方法是使用命名参数,如下面的示例所示

      public static void Main()
       {
           MyTestExample("John");
           MyTestExample("John", "Doe");
           MyTestExample("John", "Doe",40);
           MyTestExample(firstname:"John", age:67);
           Console.ReadLine();
        }

        public static void MyTestExample(string firstname,string lastname=null, int age=0)
         {
            Console.WriteLine("{0} {1} is {2} years old", firstname, lastname, age);
         }

什么是 TPL(任务并行库)?

Task Parallel Library(任务并行库)可以定义为旧的 Threading 实现的替代方案,并提供了一种方便的方式来实现多个任务同时执行。任务本身可以定义为基本的执行过程。可以通过 Task Factory 轻松创建任务,并且 Task.WaitAll WaitAny 函数有助于建立任务的层次结构,或者可以称为任务阻塞函数。例如,暂停执行直到给定任务的功能完成为止。任务可以是连续的,意味着如果主任务完成,您可以指定子任务。如果您想在主任务执行完成后执行任何功能(如日志记录、数据库保存等),此功能非常有用。以下是 Task ContinueWith WaitAll 的简单示例。

      static void Main(string[] args)
        {
            var task = Task.Factory.StartNew(() => 
            MyFirtThread(1)).ContinueWith((prevtask) => ThreadCompleted("MyFirtThread"));
            var task1 = Task.Factory.StartNew(() => MySecondThread(2));
            var task2 = Task.Factory.StartNew(() => MyFirtThread(1));
            var task3 = Task.Factory.StartNew(() => MySecondThread(2));
            var tasklist = new List<Task> {task, task1, task2, task3};
            Task.WaitAll(tasklist.ToArray());
            Console.WriteLine("press enter to exit...");
            Console.ReadLine();
        }

      public static void  MyFirtThread(int id)
        {
            Thread.Sleep(1500);
            Console.WriteLine("myFirtThread {0}",id);
        }

      public static void MySecondThread(int id)
        {
            Console.WriteLine("mySecondThread {0}",id);
        }

      public static void ThreadCompleted(string task)
        {
            Console.WriteLine(task + " completed");
        }

任务并行库中提供的 Parallel Loop 是什么?

任务并行库中提供了 ForEach For 循环,它们有助于并行运行迭代,而不是像第一个示例那样顺序执行任务(在该示例中,先执行任务,然后是任务 1 到任务 3)。Parallel.For Parallel.ForEach 自动处理 WaitAll 功能,这意味着它会阻止执行直到循环的所有迭代完成执行。以下是一个简单的示例

  static void Main(string[] args)
        {
            var intArray = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 23, 5, 7, 356, 89};
            Parallel.ForEach(intArray, (i) => Console.WriteLine(i));
            Console.WriteLine("press enter to exit...");
            Console.ReadLine();
        }

什么是任务取消令牌或如何取消任务?

由于任务是并行执行的,因此提供了非常有用的取消方法。通过 TaskCancellationToken ,可以暂停所有任务的执行或处理(日志记录、跳过任务等),如果出现任何异常。以下是 CancellationToken 的简单示例。CancellationTokenSource 生成令牌以唯一标识任务,该令牌需要作为参数传递给任务执行方法,并检查其属性以确定是否请求了 Cancellation 。为了更好地进行异常处理,最好将 Cancellation Token 传递给所有方法

       static void Main(string[] args)
         {
            var source = new CancellationTokenSource();
            var task = Task.Factory.StartNew(() => MyFirtThread(1, source.Token)).ContinueWith
            ((prevtask) => ThreadCompleted("MyFirtThread", source.Token));
            source.Cancel();
            Console.WriteLine("press enter to exit...");
            Console.ReadLine();
         }

        public static void  MyFirtThread(int id, CancellationToken token)
         {
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("Thread Cancellation Requested");
                token.ThrowIfCancellationRequested();
            }

            Thread.Sleep(1500);
            Console.WriteLine("myFirtThread {0}",id);
        }

       public static void ThreadCompleted(string task, CancellationToken token)
        {
            if (token.IsCancellationRequested)
             {
                Console.WriteLine("Thread Cancellation Requested");
                token.ThrowIfCancellationRequested();
             }

            Console.WriteLine(task + " completed");
        }

await 和 async 是什么?

await async 是与 Task Parallel Library 配合使用的结构。您仍然可以通过 Task Parallel Library (TPL) 类实现许多功能,但 await async 确实节省了大量代码,并且具有内置功能,否则需要更多编码。async 关键字用于方法签名,它仅表明该方法应使用 await 关键字异步运行。没有 await 关键字,您将收到编译器的警告,并且该方法将同步运行。await 以非阻塞方式执行代码。await async 的一个很好的例子是响应式 UI。例如,在桌面应用程序中,如果主线程仍在进行中,则无法执行子操作/线程。如果您单击任何按钮,您将无法执行任何其他操作,直到从调用方法返回响应。通过 async await ,即使主线程正在运行,您也可以让子线程继续运行,这确实提高了用户体验。await async 在许多其他场景中也很有帮助,例如调用 WebAPI 、服务或任何 Web 应用程序等。

下面给出了 Windows Form 的简单示例

  public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            PrintLabel("John Doe");
        }

        private async void PrintLabel(string name)
        {
            var result = await AssignValue(name);
            label1.Text = result;
        }

        private Task<string> AssignValue(string name)
        {
           return Task.Factory.StartNew(() => GetValue(name));
        }

        private string GetValue(string name)
        {
           Thread.Sleep(2000);
           return "Hi " + name;
        }
    }

历史

  • 2015/3/19:创建
© . All rights reserved.