Câu trả lời được chấp nhận mô tả chính xác cách danh sách nên được khai báo và rất được khuyến khích cho hầu hết các trường hợp.
Nhưng tôi đã gặp một kịch bản khác, cũng bao hàm câu hỏi được đặt ra. Điều gì sẽ xảy ra nếu bạn phải sử dụng danh sách đối tượng hiện có, như ViewData["htmlAttributes"]
trong MVC ? Làm thế nào bạn có thể truy cập các thuộc tính của nó (chúng thường được tạo thông qua new { @style="width: 100px", ... }
)?
Đối với kịch bản hơi khác này, tôi muốn chia sẻ với bạn những gì tôi đã tìm ra. Trong các giải pháp bên dưới, tôi giả sử khai báo sau cho nodes
:
List<object> nodes = new List<object>();
nodes.Add(
new
{
Checked = false,
depth = 1,
id = "div_1"
});
1. Giải pháp với động
Trong C # 4.0 và các phiên bản cao hơn , bạn có thể chỉ cần truyền sang động và viết:
if (nodes.Any(n => ((dynamic)n).Checked == false))
Console.WriteLine("found not checked element!");
Lưu ý: Điều này đang sử dụng liên kết muộn, có nghĩa là nó sẽ chỉ nhận ra trong thời gian chạy nếu đối tượng không có thuộc Checked
tính và ném một RuntimeBinderException
trong trường hợp này - vì vậy nếu bạn cố gắng sử dụng thuộc tính không tồn tại, Checked2
bạn sẽ nhận được thông báo sau tại runtime: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.
2. Giải pháp có phản ánh
Giải pháp với sự phản chiếu hoạt động cả với các phiên bản trình biên dịch C # cũ và mới . Đối với các phiên bản C # cũ, vui lòng xem gợi ý ở cuối câu trả lời này.
Lý lịch
Như một điểm khởi đầu, tôi đã tìm thấy một câu trả lời tốt ở đây . Ý tưởng là chuyển đổi kiểu dữ liệu ẩn danh thành từ điển bằng cách sử dụng phản xạ. Từ điển giúp bạn dễ dàng truy cập các thuộc tính, vì tên của chúng được lưu trữ dưới dạng khóa (bạn có thể truy cập chúng như vậy myDict["myProperty"]
).
Lấy cảm hứng từ mã kiểm tra vào liên kết ở trên, tôi tạo ra một lớp mở rộng cung cấp GetProp
, UnanonymizeProperties
và UnanonymizeListItems
như phương pháp khuyến nông, đó truy cập đơn giản hóa để tính nặc danh. Với lớp này, bạn chỉ cần thực hiện truy vấn như sau:
if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
Console.WriteLine("found not checked element!");
}
hoặc bạn có thể sử dụng biểu thức nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
làm if
điều kiện để lọc ngầm và sau đó kiểm tra xem có bất kỳ phần tử nào được trả về hay không.
Để lấy đối tượng đầu tiên chứa thuộc tính "Đã kiểm tra" và trả về thuộc tính "chiều sâu" của nó, bạn có thể sử dụng:
var depth = nodes.UnanonymizeListItems()
?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
hoặc ngắn hơn: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Lưu ý: Nếu bạn có danh sách các đối tượng không nhất thiết phải chứa tất cả các thuộc tính (ví dụ: một số không chứa thuộc tính "Đã kiểm tra") và bạn vẫn muốn tạo truy vấn dựa trên các giá trị "Đã kiểm tra", bạn có thể làm cái này:
if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true));
return y.HasValue && y.Value == false;}).Any())
{
Console.WriteLine("found not checked element!");
}
Điều này ngăn cản, điều đó KeyNotFoundException
xảy ra nếu thuộc tính "Đã kiểm tra" không tồn tại.
Lớp bên dưới chứa các phương thức mở rộng sau:
UnanonymizeProperties
: Được sử dụng để khử ẩn danh các thuộc tính có trong một đối tượng. Phương pháp này sử dụng phản xạ. Nó chuyển đổi đối tượng thành một từ điển chứa các thuộc tính và giá trị của nó.
UnanonymizeListItems
: Dùng để chuyển một danh sách các đối tượng thành danh sách các từ điển chứa các thuộc tính. Nó có thể chứa một biểu thức lambda để lọc trước.
GetProp
: Được sử dụng để trả về một giá trị duy nhất phù hợp với tên thuộc tính đã cho. Cho phép xử lý các thuộc tính không tồn tại dưới dạng giá trị null (true) chứ không phải là KeyNotFoundException (false)
Đối với các ví dụ ở trên, tất cả những gì bắt buộc là bạn thêm lớp mở rộng bên dưới:
public static class AnonymousTypeExtensions
{
// makes properties of object accessible
public static IDictionary UnanonymizeProperties(this object obj)
{
Type type = obj?.GetType();
var properties = type?.GetProperties()
?.Select(n => n.Name)
?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(obj, null));
return properties;
}
// converts object list into list of properties that meet the filterCriteria
public static List<IDictionary> UnanonymizeListItems(this List<object> objectList,
Func<IDictionary<string, object>, bool> filterCriteria=default)
{
var accessibleList = new List<IDictionary>();
foreach (object obj in objectList)
{
var props = obj.UnanonymizeProperties();
if (filterCriteria == default
|| filterCriteria((IDictionary<string, object>)props) == true)
{ accessibleList.Add(props); }
}
return accessibleList;
}
// returns specific property, i.e. obj.GetProp(propertyName)
// requires prior usage of AccessListItems and selection of one element, because
// object needs to be a IDictionary<string, object>
public static object GetProp(this object obj, string propertyName,
bool treatNotFoundAsNull = false)
{
try
{
return ((System.Collections.Generic.IDictionary<string, object>)obj)
?[propertyName];
}
catch (KeyNotFoundException)
{
if (treatNotFoundAsNull) return default(object); else throw;
}
}
}
Gợi ý: Đoạn mã trên được sử dụng null-có điều kiện khai thác, có sẵn từ C # phiên bản 6.0 - nếu bạn đang làm việc với các trình biên dịch C # cũ (ví dụ như C # 3.0), chỉ đơn giản là thay thế ?.
bằng .
và ?[
bởi [
ở khắp mọi nơi, ví dụ như
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
Nếu bạn không buộc phải sử dụng trình biên dịch C # cũ hơn, hãy giữ nguyên nó, bởi vì sử dụng null-điều kiện làm cho việc xử lý null dễ dàng hơn nhiều.
Lưu ý: Giống như các giải pháp khác với động, giải pháp này cũng đang sử dụng liên kết trễ, nhưng trong trường hợp này, bạn không gặp phải ngoại lệ - nó chỉ đơn giản là sẽ không tìm thấy phần tử nếu bạn đang đề cập đến thuộc tính không tồn tại, miễn là khi bạn giữ các toán tử điều kiện null .
Điều có thể hữu ích cho một số ứng dụng là thuộc tính được tham chiếu qua một chuỗi trong giải pháp 2, do đó nó có thể được tham số hóa.