c# 4.0引入了一种叫做“动态”的新类型。这听起来不错,但是程序员要用它来做什么呢?
在某种情况下,它是否可以挽救局面?
c# 4.0引入了一种叫做“动态”的新类型。这听起来不错,但是程序员要用它来做什么呢?
在某种情况下,它是否可以挽救局面?
当前回答
dynamic关键字是c# 4.0的新特性,用于告诉编译器变量的类型可以改变,或者直到运行时才知道。可以认为它可以与对象交互,而不必强制转换对象。
dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!
注意,我们既不需要强制转换也不需要声明cust为Customer类型。因为我们将它声明为动态的,所以运行时将接管它,然后为我们搜索并设置FirstName属性。当然,当你使用动态变量时,你就放弃了编译器类型检查。这意味着调用cast . missingmethod()将被编译,直到运行时才会失败。这个操作的结果是一个RuntimeBinderException,因为MissingMethod没有在Customer类上定义。
上面的例子展示了动态在调用方法和属性时是如何工作的。另一个强大的(潜在的危险)特性是能够为不同类型的数据重用变量。我相信Python、Ruby和Perl程序员可以想出一百万种方法来利用这一点,但我使用c#太久了,我觉得它“不对”。
dynamic foo = 123;
foo = "bar";
好吧,所以你很可能不会经常写上面这样的代码。然而,有时变量重用可以派上用场,或者可以清理遗留代码的脏部分。我经常遇到的一个简单的情况是必须不断地在小数和双精度之间进行强制转换。
decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");
第二行不能编译,因为2.5被输入为double类型,第3行不能编译,因为Math。根特期望双倍。显然,您所要做的就是强制转换和/或更改变量类型,但可能在某些情况下使用动态是有意义的。
dynamic foo = GetDecimalValue(); // still returns a decimal
foo = foo / 2.5; // The runtime takes care of this for us
foo = Math.Sqrt(foo); // Again, the DLR works its magic
string bar = foo.ToString("c");
阅读更多功能:http://www.codeproject.com/KB/cs/CSharp4Features.aspx
其他回答
它使静态类型语言(CLR)更容易与运行在DLR(动态语言运行库)上的动态语言(python, ruby…)互操作,参见MSDN:
例如,您可以使用下面的代码来增加一个计数器 在XML和c#中。 Scriptobj。SetProperty("Count", ((int)GetProperty("Count")) + 1); 通过使用DLR,可以使用下面的代码来代替 同样的操作。 scriptobj。计数+= 1;
MSDN列出了以下优点:
简化将动态语言移植到。net框架 在静态类型语言中启用动态特性 提供DLR和.NET框架的未来好处 启用库和对象的共享 提供快速动态分派和调用
详情请参阅MSDN。
我很惊讶没有人提到多重调度。解决这个问题的通常方法是通过访问者模式,这并不总是可能的,所以你最终会得到堆叠的支票。
这是我自己的一个应用实例。而不是做:
public static MapDtoBase CreateDto(ChartItem item)
{
if (item is ElevationPoint) return CreateDtoImpl((ElevationPoint)item);
if (item is MapPoint) return CreateDtoImpl((MapPoint)item);
if (item is MapPolyline) return CreateDtoImpl((MapPolyline)item);
//other subtypes follow
throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}
你该怎么做:
public static MapDtoBase CreateDto(ChartItem item)
{
return CreateDtoImpl(item as dynamic);
}
private static MapDtoBase CreateDtoImpl(ChartItem item)
{
throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}
private static MapDtoBase CreateDtoImpl(MapPoint item)
{
return new MapPointDto(item);
}
private static MapDtoBase CreateDtoImpl(ElevationPoint item)
{
return new ElevationDto(item);
}
注意,在第一种情况下,ElevationPoint是mappointment的子类,如果它没有放在mappointment之前,它将永远不会被到达。dynamic则不是这样,因为最接近的匹配方法将被调用。
正如您可能从代码中猜到的那样,当我执行从ChartItem对象到其可序列化版本的转换时,该特性非常方便。我不想用访问者污染我的代码,我也不想用无用的序列化特定属性污染我的ChartItem对象。
COM互操作。尤其是IUnknown。这是专门为它设计的。
The best use case of dynamic type variables for me was when, recently, I was writing a data access layer in ADO.NET (using SQLDataReader) and the code was invoking the already written legacy stored procedures. There are hundreds of those legacy stored procedures containing bulk of the business logic. My data access layer needed to return some sort of structured data to the business logic layer, C# based, to do some manipulations (although there are almost none). Every stored procedure returns different set of data (table columns). So instead of creating dozens of classes or structs to hold the returned data and pass it to the BLL, I wrote the below code which looks quite elegant and neat.
public static dynamic GetSomeData(ParameterDTO dto)
{
dynamic result = null;
string SPName = "a_legacy_stored_procedure";
using (SqlConnection connection = new SqlConnection("my connection string"))
{
SqlCommand command = new SqlCommand(SPName, connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("@empid", dto.EmpID));
command.Parameters.Add(new SqlParameter("@deptid", dto.DeptID));
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
dynamic row = new ExpandoObject();
row.EmpName = reader["EmpFullName"].ToString();
row.DeptName = reader["DeptName"].ToString();
row.AnotherColumn = reader["AnotherColumn"].ToString();
result = row;
}
}
}
return result;
}
你可以使用pythonnet调用动态语言,比如CPython:
充满活力。
在对泛型应用数值运算符时,可以将泛型强制转换为动态泛型。这提供了类型安全性并避免了泛型的限制。这本质上是*鸭子打字:
T y = x *(动态)x,其中typeof(x)为T