C # 4.0 đã giới thiệu một loại mới gọi là 'động'. Tất cả nghe có vẻ tốt, nhưng một lập trình viên sẽ sử dụng nó để làm gì?
Có một tình huống mà nó có thể tiết kiệm trong ngày?
C # 4.0 đã giới thiệu một loại mới gọi là 'động'. Tất cả nghe có vẻ tốt, nhưng một lập trình viên sẽ sử dụng nó để làm gì?
Có một tình huống mà nó có thể tiết kiệm trong ngày?
Câu trả lời:
Từ khóa động mới đối với C # 4.0 và được sử dụng để báo cho trình biên dịch rằng loại của biến có thể thay đổi hoặc không được biết cho đến khi chạy. Hãy nghĩ về nó như có thể tương tác với một đối tượng mà không cần phải sử dụng nó.
dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!
Lưu ý rằng chúng tôi không cần bỏ cũng như tuyên bố quyền giám hộ là loại Khách hàng. Bởi vì chúng tôi đã khai báo nó động, thời gian chạy tiếp quản và sau đó tìm kiếm và đặt thuộc tính FirstName cho chúng tôi. Bây giờ, tất nhiên, khi bạn đang sử dụng một biến động, bạn đang từ bỏ việc kiểm tra loại trình biên dịch. Điều này có nghĩa là lệnh gọi.MissingMethod () sẽ biên dịch và không thất bại cho đến khi thực thi. Kết quả của hoạt động này là RuntimeBinderException vì MissingMethod không được xác định trên lớp Khách hàng.
Ví dụ trên cho thấy cách hoạt động năng động khi gọi các phương thức và thuộc tính. Một tính năng mạnh mẽ (và có khả năng nguy hiểm) khác là có thể sử dụng lại các biến cho các loại dữ liệu khác nhau. Tôi chắc rằng các lập trình viên Python, Ruby và Perl ngoài kia có thể nghĩ ra hàng triệu cách để tận dụng lợi thế này, nhưng tôi đã sử dụng C # lâu đến nỗi nó chỉ cảm thấy "sai" với tôi.
dynamic foo = 123;
foo = "bar";
OK, vì vậy rất có thể bạn sẽ không viết mã như trên rất thường xuyên. Tuy nhiên, có thể đôi khi, việc tái sử dụng biến có thể có ích hoặc dọn sạch một đoạn mã kế thừa bẩn. Một trường hợp đơn giản tôi gặp phải thường xuyên là phải liên tục giữa thập phân và gấp đôi.
decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");
Dòng thứ hai không biên dịch vì 2.5 được gõ dưới dạng gấp đôi và dòng 3 không biên dịch vì Math.Sqrt mong đợi gấp đôi. Rõ ràng, tất cả những gì bạn phải làm là bỏ và / hoặc thay đổi loại biến của bạn, nhưng có thể có những tình huống mà động có ý nghĩa để sử dụng.
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");
Đọc thêm tính năng: http://www.codeproject.com/KB/cs/CSharp4Features.aspx
dynamic
c # để giải quyết các vấn đề có thể giải quyết (thậm chí tốt hơn) bằng các tính năng c # tiêu chuẩn và gõ tĩnh, hoặc nhiều nhất là với suy luận kiểu ( var
). chỉdynamic
nên được sử dụng khi có vấn đề về khả năng tương tác với DLR. Nếu bạn viết mã bằng ngôn ngữ gõ tĩnh, như c #, thì hãy thực hiện và không mô phỏng ngôn ngữ động. Đó chỉ là xấu xí.
dynamic
biến trong mã của mình, nơi bạn không cần đến chúng (như trong ví dụ của bạn với squareroot), bạn sẽ từ bỏ việc kiểm tra lỗi thời gian biên dịch sạch; thay vào đó bạn đang nhận được lỗi thời gian chạy có thể.
Các dynamic
từ khóa được thêm vào, cùng với nhiều tính năng mới khác của C # 4.0, để làm cho nó đơn giản để nói chuyện với mã mà cuộc sống trong hoặc xuất phát từ runtimes khác, có các API khác nhau.
Lấy một ví dụ.
Nếu bạn có một đối tượng COM, như Word.Application
đối tượng và muốn mở một tài liệu, phương thức để thực hiện đi kèm với không dưới 15 tham số, hầu hết trong số đó là tùy chọn.
Để gọi phương thức này, bạn sẽ cần một cái gì đó như thế này (tôi đang đơn giản hóa, đây không phải là mã thực tế):
object missing = System.Reflection.Missing.Value;
object fileName = "C:\\test.docx";
object readOnly = true;
wordApplication.Documents.Open(ref fileName, ref missing, ref readOnly,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing);
Lưu ý tất cả những lập luận đó? Bạn cần vượt qua những người kể từ C # trước phiên bản 4.0 không có khái niệm về các đối số tùy chọn. Trong C # 4.0, API COM đã được làm việc dễ dàng hơn bằng cách giới thiệu:
ref
tùy chọn cho API COMCú pháp mới cho cuộc gọi trên sẽ là:
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
Xem nó trông dễ dàng hơn bao nhiêu, nó trở nên dễ đọc hơn bao nhiêu?
Chúng ta hãy phá vỡ điều đó:
named argument, can skip the rest
|
v
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
^ ^
| |
notice no ref keyword, can pass
actual parameter values instead
Điều kỳ diệu là trình biên dịch C # bây giờ sẽ tiêm mã cần thiết và làm việc với các lớp mới trong thời gian chạy, để thực hiện gần như chính xác những gì bạn đã làm trước đây, nhưng cú pháp đã bị ẩn khỏi bạn, bây giờ bạn có thể tập trung vào những gì , và không quá nhiều về làm thế nào . Anders Hejlsberg thích nói rằng bạn phải thực hiện các "câu thần chú" khác nhau, đó là một kiểu chơi chữ cho phép thuật của toàn bộ, trong đó bạn thường phải vẫy tay và nói một số từ ma thuật theo đúng thứ tự để có được một loại phép thuật nhất định đi. Cách nói chuyện API cũ với các đối tượng COM là rất nhiều, bạn cần phải vượt qua rất nhiều vòng để dỗ trình biên dịch biên dịch mã cho bạn.
Mọi thứ bị phá vỡ trong C # trước phiên bản 4.0 thậm chí nhiều hơn nếu bạn cố gắng nói chuyện với một đối tượng COM mà bạn không có giao diện hoặc lớp, tất cả những gì bạn có là một IDispatch
tài liệu tham khảo.
Nếu bạn không biết nó là gì, IDispatch
về cơ bản là sự phản chiếu cho các đối tượng COM. Với một IDispatch
giao diện, bạn có thể hỏi đối tượng "số id cho phương thức được gọi là Save" là gì và xây dựng các mảng của một loại nhất định có chứa các giá trị đối số và cuối cùng gọi một Invoke
phương thức trên IDispatch
giao diện để gọi phương thức, truyền tất cả thông tin bạn đã quản lý để cùng nhau tranh giành.
Phương thức Save ở trên có thể trông như thế này (đây chắc chắn không phải là mã đúng):
string[] methodNames = new[] { "Open" };
Guid IID = ...
int methodId = wordApplication.GetIDsOfNames(IID, methodNames, methodNames.Length, lcid, dispid);
SafeArray args = new SafeArray(new[] { fileName, missing, missing, .... });
wordApplication.Invoke(methodId, ... args, ...);
Tất cả điều này chỉ để mở một tài liệu.
VB đã có các đối số tùy chọn và hỗ trợ cho hầu hết những điều này từ lâu, vì vậy mã C # này:
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
về cơ bản chỉ là C # bắt kịp VB về tính biểu cảm, nhưng thực hiện nó đúng cách, bằng cách làm cho nó có thể mở rộng và không chỉ cho COM. Tất nhiên điều này cũng có sẵn cho VB.NET hoặc bất kỳ ngôn ngữ nào khác được xây dựng trên đầu thời gian chạy .NET.
Bạn có thể tìm thêm thông tin về IDispatch
giao diện trên Wikipedia: IDispatch nếu bạn muốn đọc thêm về nó. Nó thực sự là thứ linh tinh.
Tuy nhiên, nếu bạn muốn nói chuyện với một đối tượng Python thì sao? Có một API khác với API được sử dụng cho các đối tượng COM và vì các đối tượng Python cũng có tính chất động, bạn cần phải sử dụng phép thuật phản chiếu để tìm đúng phương thức để gọi, tham số của chúng, v.v. nhưng không phải là .NET Sự phản chiếu, một cái gì đó được viết cho Python, khá giống mã IDispatch ở trên, hoàn toàn khác nhau.
Và đối với Ruby? Một API khác vẫn còn.
JavaScript? Cùng một giao dịch, API khác nhau cho điều đó là tốt.
Từ khóa động bao gồm hai điều:
dynamic
dynamic
từ khóa yêu cầu và ánh xạ các cuộc gọi đến đúng cách thực hiện. API thậm chí còn được ghi lại, vì vậy nếu bạn có các đối tượng xuất phát từ thời gian chạy không được bảo hiểm, bạn có thể thêm nó.Các dynamic
từ khóa được không, tuy nhiên, có nghĩa là để thay thế bất kỳ mã .NET chỉ đang tồn tại. Chắc chắn, bạn có thể làm điều đó, nhưng nó không được thêm vào vì lý do đó, và các tác giả của ngôn ngữ lập trình C # với Anders Hejlsberg ở phía trước, đã kiên quyết nhất rằng họ vẫn coi C # là ngôn ngữ được đánh máy mạnh mẽ và sẽ không hy sinh nguyên tắc đó.
Điều này có nghĩa là mặc dù bạn có thể viết mã như thế này:
dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;
và có nó biên dịch, nó không có nghĩa là một loại hệ thống ma thuật cho phép-tìm-ra-những gì bạn có nghĩa là trong thời gian chạy.
Toàn bộ mục đích là để làm cho việc nói chuyện với các loại đối tượng khác dễ dàng hơn.
Có rất nhiều tài liệu trên internet về từ khóa, người đề xướng, người phản đối, thảo luận, tán tỉnh, khen ngợi, v.v.
Tôi khuyên bạn nên bắt đầu với các liên kết sau và sau đó google để biết thêm:
dynamic
đã được thêm vào, để hỗ trợ các hệ sinh thái khác về cách gọi phương thức giống như phản xạ có thể được thực hiện, cũng như cung cấp một cách tiếp cận hộp đen cho các cấu trúc dữ liệu với cách thức được chứng minh bằng tài liệu này.
Tôi ngạc nhiên rằng không ai đề cập đến nhiều công văn . Cách thông thường để giải quyết vấn đề này là thông qua mẫu Khách truy cập và điều đó không phải lúc nào cũng có thể để bạn kết thúc với các is
kiểm tra xếp chồng .
Vì vậy, đây là một ví dụ thực tế của một ứng dụng của riêng tôi. Thay vì làm:
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());
}
Bạn làm:
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);
}
Lưu ý rằng trong trường hợp đầu tiên ElevationPoint
là lớp con của MapPoint
và nếu nó không được đặt trước MapPoint
thì nó sẽ không bao giờ đạt được. Đây không phải là trường hợp động, vì phương thức khớp gần nhất sẽ được gọi.
Như bạn có thể đoán từ mã, tính năng đó trở nên tiện dụng trong khi tôi đang thực hiện dịch từ các đối tượng ChartItem sang các phiên bản tuần tự hóa của chúng. Tôi không muốn làm ô nhiễm mã của mình với khách truy cập và tôi cũng không muốn làm ô nhiễm ChartItem
các đối tượng của mình với các thuộc tính cụ thể tuần tự hóa vô dụng.
is
chồng lên nhau.
magic
; không có thứ gọi là ma thuật
Nó giúp các ngôn ngữ gõ tĩnh (CLR) dễ dàng tương tác với các ngôn ngữ động (python, ruby ...) chạy trên DLR (thời gian chạy ngôn ngữ động), xem MSDN :
Ví dụ: bạn có thể sử dụng đoạn mã sau để tăng bộ đếm trong XML bằng C #.
Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);
Bằng cách sử dụng DLR, bạn có thể sử dụng mã sau đây cho cùng một hoạt động.
scriptobj.Count += 1;
MSDN liệt kê những ưu điểm này:
- Đơn giản hóa việc chuyển ngôn ngữ động sang .NET Framework
- Cho phép các tính năng động trong các ngôn ngữ được nhập tĩnh
- Cung cấp các lợi ích trong tương lai của DLR và .NET Framework
- Cho phép chia sẻ thư viện và đối tượng
- Cung cấp công văn và yêu cầu nhanh
Xem MSDN để biết thêm chi tiết.
Một ví dụ về sử dụng:
Bạn tiêu thụ nhiều lớp có thuộc tính chung 'CreationDate':
public class Contact
{
// some properties
public DateTime CreationDate { get; set; }
}
public class Company
{
// some properties
public DateTime CreationDate { get; set; }
}
public class Opportunity
{
// some properties
public DateTime CreationDate { get; set; }
}
Nếu bạn viết một phương thức cộng đồng lấy giá trị của Thuộc tính 'CreationDate', bạn phải sử dụng sự phản chiếu:
static DateTime RetrieveValueOfCreationDate(Object item)
{
return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
}
Với khái niệm 'động', mã của bạn thanh lịch hơn nhiều:
static DateTime RetrieveValueOfCreationDate(dynamic item)
{
return item.CreationDate;
}
Nó chủ yếu sẽ được sử dụng bởi các nạn nhân RAD và Python để phá hủy chất lượng mã, IntelliSense và biên dịch phát hiện lỗi thời gian.
Nó đánh giá trong thời gian chạy, vì vậy bạn có thể chuyển loại như bạn có thể trong JavaScript sang bất cứ thứ gì bạn muốn. Điều này hợp pháp:
dynamic i = 12;
i = "text";
Và vì vậy bạn có thể thay đổi loại khi bạn cần. Sử dụng nó như là một phương sách cuối cùng; nó có lợi, nhưng tôi nghe nói rất nhiều điều xảy ra trong bối cảnh IL được tạo ra và điều đó có thể có giá thực hiện.
Trường hợp sử dụng tốt nhất các biến loại 'động' đối với tôi là khi gần đây, tôi đang viết một lớp truy cập dữ liệu trong ADO.NET ( sử dụng SQLDataReader ) và mã đã gọi các thủ tục lưu trữ đã được viết sẵn. Có hàng trăm thủ tục lưu trữ kế thừa chứa phần lớn logic nghiệp vụ. Lớp truy cập dữ liệu của tôi cần để trả về một số loại dữ liệu có cấu trúc cho lớp logic nghiệp vụ, dựa trên C #, để thực hiện một số thao tác ( mặc dù hầu như không có ). Mỗi thủ tục được lưu trữ trả về tập dữ liệu khác nhau ( cột bảng ). Vì vậy, thay vì tạo ra hàng tá lớp hoặc cấu trúc để giữ dữ liệu được trả về và chuyển nó đến BLL, tôi đã viết đoạn mã dưới đây trông khá thanh lịch và gọn gàng.
public static dynamic GetSomeData(ParameterDTO dto)
{
dynamic result = null;
string SPName = "a_legacy_stored_procedure";
using (SqlConnection connection = new SqlConnection(DataConnection.ConnectionString))
{
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;
}
dynamic np = Py.Import("numpy")
dynamic
khi áp dụng toán tử số trên chúng. Điều này cung cấp loại an toàn và tránh những hạn chế của thuốc generic. Đây là bản chất * gõ vịt:T y = x * (dynamic)x
, Ở đâu typeof(x) is T
Một trường hợp sử dụng khác để dynamic
gõ là cho các phương thức ảo gặp vấn đề với hiệp phương sai hoặc chống chỉ định. Một ví dụ như vậy là Clone
phương thức khét tiếng trả về một đối tượng cùng loại với đối tượng mà nó được gọi. Vấn đề này không được giải quyết hoàn toàn bằng trả về động vì nó bỏ qua việc kiểm tra kiểu tĩnh, nhưng ít nhất bạn không cần phải sử dụng các phôi xấu xí mọi lúc như khi sử dụng đơn giản object
. Mặt khác để nói, các diễn viên trở nên ngầm.
public class A
{
// attributes and constructor here
public virtual dynamic Clone()
{
var clone = new A();
// Do more cloning stuff here
return clone;
}
}
public class B : A
{
// more attributes and constructor here
public override dynamic Clone()
{
var clone = new B();
// Do more cloning stuff here
return clone;
}
}
public class Program
{
public static void Main()
{
A a = new A().Clone(); // No cast needed here
B b = new B().Clone(); // and here
// do more stuff with a and b
}
}