我的情况很简单。在我的代码中,我有这样的代码:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

//How to do this?
if (myVariable.MyProperty.Exists)   
//Do stuff

因此,基本上我的问题是如何检查(不抛出异常)某个属性在我的动态变量上是可用的。我可以使用GetType(),但我宁愿避免这样做,因为我并不真正需要知道对象的类型。我真正想知道的是是否有一个属性(或方法,如果这使工作更简单的话)可用。指针吗?


当前回答

Denis的回答让我想到了另一个使用JsonObjects的解决方案,

头属性检查器:

Predicate<object> hasHeader = jsonObject =>
                                 ((JObject)jsonObject).OfType<JProperty>()
                                     .Any(prop => prop.Name == "header");

或许更好:

Predicate<object> hasHeader = jsonObject =>
                                 ((JObject)jsonObject).Property("header") != null;

例如:

dynamic json = JsonConvert.DeserializeObject(data);
string header = hasHeader(json) ? json.header : null;

其他回答

我认为没有办法在不尝试访问动态变量的情况下发现动态变量是否有某个成员,除非你在c#编译器中重新实现动态绑定的处理方式。这可能包含很多猜测,因为根据c#规范,它是由实现定义的。

所以你应该尝试访问成员并捕获异常,如果它失败了:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

try
{
    var x = myVariable.MyProperty;
    // do stuff with x
}
catch (RuntimeBinderException)
{
    //  MyProperty doesn't exist
} 

如果你的用例是转换一个api响应,只携带一些字段,你可以使用这个:

var template = new { address = new { street = "" } };
var response = JsonConvert.DeserializeAnonymousType(await result.Content.ReadAsStringAsync(), template);

string street = response?.address?.street;

由于ExpandoObject继承了IDictionary<string对象>,您可以使用以下检查

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

if (((IDictionary<string, object>)myVariable).ContainsKey("MyProperty"))    
//Do stuff

您可以创建一个实用程序方法来执行此检查,这将使代码更加清晰和可重用

我想我应该比较一下Martijn和svick的答案……

下面的程序返回以下结果:

Testing with exception: 2430985 ticks
Testing with reflection: 155570 ticks

void Main()
{
    var random = new Random(Environment.TickCount);

    dynamic test = new Test();

    var sw = new Stopwatch();

    sw.Start();

    for (int i = 0; i < 100000; i++)
    {
        TestWithException(test, FlipCoin(random));
    }

    sw.Stop();

    Console.WriteLine("Testing with exception: " + sw.ElapsedTicks.ToString() + " ticks");

    sw.Restart();

    for (int i = 0; i < 100000; i++)
    {
        TestWithReflection(test, FlipCoin(random));
    }

    sw.Stop();

    Console.WriteLine("Testing with reflection: " + sw.ElapsedTicks.ToString() + " ticks");
}

class Test
{
    public bool Exists { get { return true; } }
}

bool FlipCoin(Random random)
{
    return random.Next(2) == 0;
}

bool TestWithException(dynamic d, bool useExisting)
{
    try
    {
        bool result = useExisting ? d.Exists : d.DoesntExist;
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

bool TestWithReflection(dynamic d, bool useExisting)
{
    Type type = d.GetType();

    return type.GetProperties().Any(p => p.Name.Equals(useExisting ? "Exists" : "DoesntExist"));
}

因此,我建议使用反射。见下文。


回应bland的评论:

比率是反射:100000次迭代的异常滴答声:

Fails 1/1: - 1:43 ticks
Fails 1/2: - 1:22 ticks
Fails 1/3: - 1:14 ticks
Fails 1/5: - 1:9 ticks
Fails 1/7: - 1:7 ticks
Fails 1/13: - 1:4 ticks
Fails 1/17: - 1:3 ticks
Fails 1/23: - 1:2 ticks
...
Fails 1/43: - 1:2 ticks
Fails 1/47: - 1:1 ticks

...很公平——如果你认为它失败的概率小于~1/47,那么就去找例外。


上面假设您每次都运行GetProperties()。您可以通过将每种类型的GetProperties()结果缓存到字典或类似内容中来加快该过程。如果您要反复检查同一组类型,这可能会有所帮助。

下面是另一种方法:

using Newtonsoft.Json.Linq;

internal class DymanicTest
{
    public static string Json = @"{
            ""AED"": 3.672825,
            ""AFN"": 56.982875,
            ""ALL"": 110.252599,
            ""AMD"": 408.222002,
            ""ANG"": 1.78704,
            ""AOA"": 98.192249,
            ""ARS"": 8.44469
}";

    public static void Run()
    {
        dynamic dynamicObject = JObject.Parse(Json);

        foreach (JProperty variable in dynamicObject)
        {
            if (variable.Name == "AMD")
            {
                var value = variable.Value;
            }
        }
    }
}