C#4.0新特性

本文介绍了Visual C#2010中引入的新特性,包括动态支持、命名及可选实参、以及泛型的协变和逆变等。通过示例详细解释了这些特性的使用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、动态支持(dynamic)
1.1、概述

  Visual C# 2010 引入了一个新类型 dynamic。 该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查。 大多数情况下,该对象就像具有类型 object 一样。 在编译时,将假定类型化为 dynamic 的元素支持任何操作。 因此,您不必考虑对象是从 COM API、从动态语言(例如 IronPython)、从 HTML 文档对象模型 (DOM)、从反射还是从程序中的其他位置获取自己的值。 但是,如果代码无效,则在运行时会捕获到错误。

  如果以下代码中的实例方法 exampleMethod1 只有一个形参,则编译器会将对该方法的第一个调用 ec.exampleMethod1(10, 4) 识别为无效,原因是它包含两个实参。 该调用将导致编译器错误。 编译器不会检查对该方法的第二个调用 dynamic_ec.exampleMethod1(10, 4),原因是 dynamic_ec 的类型为 dynamic。 因此,不会报告编译器错误。 但是,该错误不会被无限期疏忽。 它将在运行时被捕获,并导致运行时异常 

static void Main(string[] args)
{
    ExampleClass ec = new ExampleClass();
    // 下面的方面引起编译错误,如果类中只有一个参数的方法
    // one parameter.
    //ec.exampleMethod1(10, 4);

    dynamic dynamic_ec = new ExampleClass();
    // 下面代码在编译时不会被定义为一个错误,但是它引起一个运行时异常
    dynamic_ec.exampleMethod1(10, 4);

    // 下面代码不会引起编译异常,无论方法存在不
    dynamic_ec.someMethod("some argument", 7, null);
    dynamic_ec.nonexistentMethod();
}
2、命名实参和可选实参
2.1、概述

  Visual C# 2010 引入了命名实参和可选实参。 利用“命名实参”,您将能够为特定形参指定实参,方法是将实参与该形参的名称关联,而不是与形参在形参列表中的位置关联。 利用“可选实参”,您将能够为某些形参省略实参。 这两种技术都可与方法、索引器、构造函数和委托一起使用。
  在使用命名实参和可选实参时,将按实参出现在实参列表(而不是形参列表)中的顺序计算这些实参。

  命名形参和可选形参一起使用时,您将能够只为可选形参列表中的少数形参提供实参
  
  命名参数Demo

 class Program
    {
        static int CalculateBMI(int weight, int height)
        {
            return (weight * 703) / (height * height);
        }
        static void Main(string[] args)
        {
            // The method can be called in the normal way, by using positional arguments.
            Console.WriteLine(CalculateBMI(123, 64));

            // Named arguments can be supplied for the parameters in either order.
            Console.WriteLine(CalculateBMI(weight: 123, height: 64));
            Console.WriteLine(CalculateBMI(height: 64, weight: 123));

            // Positional arguments cannot follow named arguments.
            // The following statement causes a compiler error.
            //Console.WriteLine(CalculateBMI(weight: 123, 64));

            // Named arguments can follow positional arguments.
            Console.WriteLine(CalculateBMI(123, height: 64));
        }
    }
2.2、可选参数概述

  方法、构造函数、索引器或委托的定义可以指定其形参为必需还是可选。 任何调用都必须为所有必需的形参提供实参,但可以为可选的形参省略实参。

  每个可选形参都具有默认值作为其定义的一部分。 如果没有为该形参发送实参,则使用默认值。 默认值必须为常量。

  可选形参在形参列表的末尾定义,位于任何必需的形参之后。 如果调用方为一系列可选形参中的任意一个形参提供了实参,则它必须为前面的所有可选形参提供实参。 实参列表中不支持使用逗号分隔的间隔。 例如,在以下代码中,使用一个必选形参和两个可选形参定义实例方法

class OptionalExample
    {
        static void Main(string[] args)
        {
            // Instance anExample does not send an argument for the constructor's
            // optional parameter.
            ExampleClass anExample = new ExampleClass();
            anExample.ExampleMethod(1, "One", 1);
            anExample.ExampleMethod(2, "Two");
            anExample.ExampleMethod(3);
            anExample.ExampleMethod(3, optionalint: 4);

            // Instance anotherExample sends an argument for the constructor's
            // optional parameter.
            ExampleClass anotherExample = new ExampleClass("Provided name");
            anotherExample.ExampleMethod(1, "One", 1);
            anotherExample.ExampleMethod(2, "Two");
            anotherExample.ExampleMethod(3);

            // The following statements produce compiler errors.

            // An argument must be supplied for the first parameter, and it
            // must be an integer.
            //anExample.ExampleMethod("One", 1);
            //anExample.ExampleMethod();

            // You cannot leave a gap in the provided arguments. 
            //anExample.ExampleMethod(3, ,4);
            //anExample.ExampleMethod(3, 4);

            // You can use a named parameter to make the previous 
            // statement work.
            anExample.ExampleMethod(3, optionalint: 4);
        }
    }

    class ExampleClass
    {
        private string _name;

        // Because the parameter for the constructor, name, has a default
        // value assigned to it, it is optional.
        public ExampleClass(string name = "Default name")
        {
            _name = name;
        }

        // The first parameter, required, has no default value assigned
        // to it. Therefore, it is not optional. Both optionalstr and 
        // optionalint have default values assigned to them. They are optional.
        public void ExampleMethod(int required, string optionalstr = "default string",
            int optionalint = 10)
        {
            Console.WriteLine("{0}: {1}, {2}, and {3}.", _name, required, optionalstr,
                optionalint);
        }
    }

    // The output from this example is the following:
    // Default name: 1, One, and 1.
    // Default name: 2, Two, and 10.
    // Default name: 3, default string, and 10.
    // Provided name: 1, One, and 1.
    // Provided name: 2, Two, and 10.
    // Provided name: 3, default string, and 10.
    // Default name: 3, default string, and 4.

3、协变和逆变(泛型)
3.1、概述

  在 C# 和 Visual Basic 中,协变和逆变允许数组类型、委托类型和泛型类型参数进行隐式引用转换。 协变保留分配兼容性,逆变与之相反。
  
  这个在C#2.0中就已经支持委托的协变和逆变了,C#4.0开始支持针对泛型接口的协变和逆变。
  下面的代码演示分配兼容性、协变和逆变之间的差异。
  

/ Assignment compatibility. 
string str = "test";
// An object of a more derived type is assigned to an object of a less derived type. 
object obj = str;

// 协变. 
IEnumerable<string> strings = new List<string>();
// An object that is instantiated with a more derived type argument 
// is assigned to an object instantiated with a less derived type argument. 
// Assignment compatibility is preserved. 
IEnumerable<object> objects = strings;

// 逆变.           
// Assume that the following method is in the class: 
// static void SetObject(object o) { } 
Action<object> actObject = SetObject;
// An object that is instantiated with a less derived type argument 
// is assigned to an object instantiated with a more derived type argument. 
// Assignment compatibility is reversed. 
Action<string> actString = actObject;

数组的协变允许派生程度更大的类型的数组隐式转换为派生程度更小的类型的数组。 但是此操作不是类型安全的操作,如下面的代码示例所示。

object[] array = new String[10];
// 下面预计引起一个允许是异常
// array[0] = 10;

下面的代码示例演示对方法组的协变和逆变支持。

static object GetObject() { return null; }
static void SetObject(object obj) { }

static string GetString() { return ""; }
static void SetString(string str) { }

static void Test()
{
    // Covariance. A delegate specifies a return type as object,
    // but you can assign a method that returns a string.
    Func<object> del = GetString;

    // Contravariance. A delegate specifies a parameter type as string,
    // but you can assign a method that takes an object.
    Action<string> del2 = SetObject;
}

下面的代码示例演示泛型接口的隐式引用转换。

IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;

参考资料:
1、Visual C# 2010 中的新增功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值