ในจาวาสคริปต์คุณสามารถตรวจพบว่ามีการกำหนดคุณสมบัติโดยใช้คำสำคัญที่ไม่ได้กำหนด:
if( typeof data.myProperty == "undefined" ) ...
คุณจะทำเช่นนี้ใน C # โดยใช้คำหลักแบบไดนามิกที่มีExpandoObject
และไม่มีข้อยกเว้นได้อย่างไร
ในจาวาสคริปต์คุณสามารถตรวจพบว่ามีการกำหนดคุณสมบัติโดยใช้คำสำคัญที่ไม่ได้กำหนด:
if( typeof data.myProperty == "undefined" ) ...
คุณจะทำเช่นนี้ใน C # โดยใช้คำหลักแบบไดนามิกที่มีExpandoObject
และไม่มีข้อยกเว้นได้อย่างไร
คำตอบ:
ตามMSDNการประกาศแสดงให้เห็นว่ามีการใช้ IDictionary:
public sealed class ExpandoObject : IDynamicMetaObjectProvider,
IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>,
IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged
คุณสามารถใช้สิ่งนี้เพื่อดูว่ามีการกำหนดสมาชิก:
var expandoObject = ...;
if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
// expandoObject.SomeMember exists.
}
ความแตกต่างที่สำคัญจะต้องทำที่นี่
คำตอบส่วนใหญ่ที่นี่เฉพาะสำหรับ ExpandoObject ที่กล่าวถึงในคำถาม แต่การใช้งานทั่วไป (และเหตุผลในการถามคำถามนี้เมื่อทำการค้นหา) คือเมื่อใช้ ASP.Net MVC ViewBag นั่นเป็นการนำไปใช้งาน / คลาสย่อยแบบกำหนดเองของ DynamicObject ซึ่งจะไม่แสดงข้อยกเว้นเมื่อคุณตรวจสอบชื่อคุณสมบัติใด ๆ ที่เป็นค่าว่าง สมมติว่าคุณอาจประกาศคุณสมบัติเช่น:
@{
ViewBag.EnableThinger = true;
}
จากนั้นสมมติว่าคุณต้องการตรวจสอบค่าและตั้งค่าว่ามีอยู่หรือไม่ ต่อไปนี้ถูกต้องจะรวบรวมไม่โยนข้อยกเว้นใด ๆ และให้คำตอบที่ถูกต้อง:
if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
// Do some stuff when EnableThinger is true
}
ตอนนี้กำจัดการประกาศของ EnableThinger รหัสเดียวกันรวบรวมและทำงานอย่างถูกต้อง ไม่จำเป็นต้องไตร่ตรอง
ซึ่งแตกต่างจาก ViewBag นั้น ExpandoObject จะส่งผลต่อหากคุณตรวจสอบค่าว่างของคุณสมบัติที่ไม่มีอยู่ ในการที่จะทำให้ฟังก์ชั่นของ MVC ViewBag ออกมาจากdynamic
วัตถุของคุณได้อย่างราบรื่นคุณจะต้องใช้การนำไปใช้ของไดนามิกที่ไม่ได้เกิดขึ้น
คุณสามารถใช้การดำเนินการที่แน่นอนใน MVC ViewBag:
. . .
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = ViewData[binder.Name];
// since ViewDataDictionary always returns a result even if the key does not exist, always return true
return true;
}
. . .
คุณสามารถดูว่ามีการเชื่อมโยงกับมุมมอง MVC ที่นี่ใน MVC ViewPage:
http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/ViewPage.cs
กุญแจสำคัญในการทำงานที่สง่างามของ DynamicViewDataDictionary คือการใช้พจนานุกรมใน ViewDataDictionary ที่นี่:
public object this[string key]
{
get
{
object value;
_innerDictionary.TryGetValue(key, out value);
return value;
}
set { _innerDictionary[key] = value; }
}
https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/ViewDataDictionary.cs
กล่าวอีกนัยหนึ่งมันจะคืนค่าให้กับคีย์ทั้งหมดเสมอโดยไม่คำนึงว่ามีอะไรอยู่ในนั้น - มันจะคืนค่าว่างเมื่อไม่มีสิ่งใดอยู่ในนั้น แต่ ViewDataDictionary มีภาระในการผูกติดอยู่กับแบบจำลองของ MVC ดังนั้นจึงเป็นการดีกว่าที่จะแยกส่วนพจนานุกรมที่สวยงามเพื่อใช้งานนอก MVC Views
มันยาวเกินไปที่จะโพสต์ความกล้าทั้งหมดที่นี่ - ส่วนใหญ่เป็นเพียงการใช้ IDictionary - แต่นี่เป็นวัตถุแบบไดนามิก (คลาสDDict
) ที่ไม่ส่งผลต่อการตรวจสอบคุณสมบัติที่ไม่ได้ประกาศบน Github:
https://github.com/b9chris/GracefulDynamicDictionary
ถ้าคุณเพียงต้องการที่จะเพิ่มให้กับโครงการของคุณผ่านทาง NuGet ชื่อของมันคือGracefulDynamicDictionary
ฉันตอบคำถามที่คล้ายกันเมื่อเร็ว ๆ นี้: ฉันจะสะท้อนถึงสมาชิกของวัตถุแบบไดนามิกได้อย่างไร
ในไม่ช้า ExpandoObject ไม่ได้เป็นเพียงวัตถุไดนามิกที่คุณอาจได้รับ การสะท้อนจะทำงานกับประเภทคงที่ (ชนิดที่ไม่ได้ใช้ IDynamicMetaObjectProvider) สำหรับประเภทที่ใช้อินเตอร์เฟสนี้การสะท้อนกลับนั้นไร้ประโยชน์ สำหรับ ExpandoObject คุณสามารถตรวจสอบได้ว่าคุณสมบัติถูกกำหนดเป็นคีย์ในพจนานุกรมพื้นฐานหรือไม่ สำหรับการใช้งานอื่น ๆ มันอาจจะท้าทายและบางครั้งวิธีเดียวที่จะทำงานกับข้อยกเว้น สำหรับรายละเอียดโปรดไปที่ลิงก์ด้านบน
ปรับปรุง: คุณสามารถใช้ผู้ได้รับมอบหมายและพยายามที่จะรับค่าจากคุณสมบัติวัตถุแบบไดนามิกถ้ามันมีอยู่ หากไม่มีคุณสมบัติให้ตรวจสอบข้อยกเว้นและส่งคืนค่าเท็จ
ลองดูมันใช้ได้ดีสำหรับฉัน:
class Program
{
static void Main(string[] args)
{
dynamic userDynamic = new JsonUser();
Console.WriteLine(IsPropertyExist(() => userDynamic.first_name));
Console.WriteLine(IsPropertyExist(() => userDynamic.address));
Console.WriteLine(IsPropertyExist(() => userDynamic.last_name));
}
class JsonUser
{
public string first_name { get; set; }
public string address
{
get
{
throw new InvalidOperationException("Cannot read property value");
}
}
}
static bool IsPropertyExist(GetValueDelegate getValueMethod)
{
try
{
//we're not interesting in the return value. What we need to know is whether an exception occurred or not
getValueMethod();
return true;
}
catch (RuntimeBinderException)
{
// RuntimeBinderException occurred during accessing the property
// and it means there is no such property
return false;
}
catch
{
//property exists, but an exception occurred during getting of a value
return true;
}
}
delegate string GetValueDelegate();
}
ผลลัพธ์ของรหัสต่อไปนี้:
True
True
False
IsPropertyExist
ที่จะ InvalidOperationException
ในตัวอย่างนี้คุณรู้ว่าใครสามารถโยน ในทางปฏิบัติคุณไม่ทราบว่าอาจมีข้อยกเว้นเกิดขึ้น +1 เพื่อต่อต้านลัทธิคาร์โก้
ฉันต้องการสร้างวิธีการขยายดังนั้นฉันสามารถทำสิ่งที่ชอบ:
dynamic myDynamicObject;
myDynamicObject.propertyName = "value";
if (myDynamicObject.HasProperty("propertyName"))
{
//...
}
... แต่คุณไม่สามารถสร้างส่วนขยายExpandoObject
ตามเอกสาร C # 5 (ข้อมูลเพิ่มเติมที่นี่ )
ดังนั้นฉันสิ้นสุดการสร้างผู้ช่วยในชั้นเรียน:
public static class ExpandoObjectHelper
{
public static bool HasProperty(ExpandoObject obj, string propertyName)
{
return ((IDictionary<String, object>)obj).ContainsKey(propertyName);
}
}
วิธีใช้:
// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
...
}
ทำไมคุณไม่ต้องการใช้ Reflection เพื่อรับชุดคุณสมบัติประเภท? แบบนี้
dynamic v = new Foo();
Type t = v.GetType();
System.Reflection.PropertyInfo[] pInfo = t.GetProperties();
if (Array.Find<System.Reflection.PropertyInfo>(pInfo, p => { return p.Name == "PropName"; }). GetValue(v, null) != null))
{
//PropName initialized
}
วิธีการขยายนี้จะตรวจสอบการมีอยู่ของคุณสมบัติแล้วส่งคืนค่าหรือ null สิ่งนี้มีประโยชน์หากคุณไม่ต้องการให้แอปพลิเคชันของคุณโยนข้อยกเว้นที่ไม่จำเป็นออกไปอย่างน้อยคุณสามารถช่วยได้
public static object Value(this ExpandoObject expando, string name)
{
var expandoDic = (IDictionary<string, object>)expando;
return expandoDic.ContainsKey(name) ? expandoDic[name] : null;
}
หากสามารถใช้งานได้เช่น:
// lookup is type 'ExpandoObject'
object value = lookup.Value("MyProperty");
หรือถ้าตัวแปรท้องถิ่นของคุณเป็น 'ไดนามิก' คุณจะต้องส่งมันไปยัง ExpandoObject ก่อน
// lookup is type 'dynamic'
object value = ((ExpandoObject)lookup).Value("PropertyBeingTested");
ทั้งนี้ขึ้นอยู่กับกรณีการใช้งานของคุณหากถือได้ว่าเป็นโมฆะเหมือนกันไม่ได้กำหนดคุณสามารถเปลี่ยน ExpandoObject ของคุณให้เป็น DynamicJsonObject
dynamic x = new System.Web.Helpers.DynamicJsonObject(new ExpandoObject());
x.a = 1;
x.b = 2.50;
Console.WriteLine("a is " + (x.a ?? "undefined"));
Console.WriteLine("b is " + (x.b ?? "undefined"));
Console.WriteLine("c is " + (x.c ?? "undefined"));
เอาท์พุท:
a is 1
b is 2.5
c is undefined
(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");
สวัสดีพวกเขาหยุดใช้ Reflection กับทุกสิ่งที่มีค่าใช้จ่ายมากรอบ CPU
นี่คือทางออก:
public class DynamicDictionary : DynamicObject
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
public int Count
{
get
{
return dictionary.Count;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name;
if (!dictionary.TryGetValue(binder.Name, out result))
result = "undefined";
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
}
ลองอันนี้
public bool PropertyExist(object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName) != null;
}
dynamic
วัตถุใด ๆ (กลับมาเสมอnull
)
data.myProperty
; มันตรวจสอบสิ่งที่typeof data.myProperty
ส่งคืน มันเป็นที่ถูกต้องที่data.myProperty
อาจมีอยู่และจะกำหนดให้undefined
แต่ในกรณีที่จะกลับสิ่งอื่นที่ไม่ใช่typeof
"undefined"
ดังนั้นรหัสนี้ใช้งานได้