C# 测试动态变量上的属性是否可用

C# 测试动态变量上的属性是否可用,c#,dynamic,dynamic-keyword,C#,Dynamic,Dynamic Keyword,我的情况很简单。在我的代码中,我有这样的代码: dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame(); //How to do this? if (myVariable.MyProperty.Exists) //Do stuff 所以,基本上我的问题是如何检查(不抛出异常)某个属性在我的动态变量上是否可用。我可以做GetType(),但我宁愿避免这样做,因为我并不真正需要知道对象的类型。我真正想知道的是,是否有一

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

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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

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

我认为,如果不尝试访问某个
动态
变量,就无法确定该变量是否具有某个成员,除非您在C#编译器中重新实现了处理动态绑定的方式。这可能包括很多猜测,因为根据C#规范,它是实现定义的

因此,如果异常失败,您应该尝试访问该成员并捕获异常:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

try
{
    var x = myVariable.MyProperty;
    // do stuff with x
}
catch (RuntimeBinderException)
{
    //  MyProperty doesn't exist
} 
也许可以使用反射

dynamic myVar = GetDataThatLooksVerySimilarButNotTheSame();
Type typeOfDynamic = myVar.GetType();
bool exist = typeOfDynamic.GetProperties().Where(p => p.Name.Equals("PropertyName")).Any(); 

如果您控制作为动态使用的类型,您不能为每个属性访问返回一个元组而不是一个值吗?类似于

public class DynamicValue<T>
{
    internal DynamicValue(T value, bool exists)
    {
         Value = value;
         Exists = exists;
    }

    T Value { get; private set; }
    bool Exists { get; private set; }
}
公共类动态值
{
内部动态值(T值,bool存在)
{
价值=价值;
存在=存在;
}
T值{get;private set;}
布尔存在{get;private set;}
}
可能是一个幼稚的实现,但如果您每次在内部构造其中一个,并返回该值而不是实际值,则可以在每个属性访问上检查
是否存在
,如果不存在,则单击
,如果值为
默认值(T)
(不相关)


这就是说,我可能缺少一些关于动态如何工作的知识,这可能不是一个可行的建议。

好吧,我遇到了一个类似的问题,但在单元测试方面

使用SharpTestsEx可以检查是否存在属性。我使用这个测试我的控制器,因为JSON对象是动态的,有人可能会更改名称,而忘记在javascript或其他东西中更改它,因此在编写控制器时测试所有属性应该会提高我的安全性

例如:

dynamic testedObject = new ExpandoObject();
testedObject.MyName = "I am a testing object";
现在,使用SharTestsEx:

Executing.This(delegate {var unused = testedObject.MyName; }).Should().NotThrow();
Executing.This(delegate {var unused = testedObject.NotExistingProperty; }).Should().Throw();
使用这个,我使用“Should().NotThrow()”测试所有现有属性


这可能不符合主题,但对某人有用。

我想我应该对和做一个比较

以下程序返回以下结果:

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

…很公平-如果您希望它以小于1/47的概率失败,那么请选择例外


以上假设您每次都在运行
GetProperties()
。您可以通过在字典或类似文件中缓存每种类型的
GetProperties()
结果来加快处理速度。如果您反复检查同一组类型,这可能会有所帮助。

对我来说,这很有效:

if (IsProperty(() => DynamicObject.MyProperty))
  ; // do stuff



delegate string GetValueDelegate();

private bool IsProperty(GetValueDelegate getValueMethod)
{
    try
    {
        //we're not interesting in the return value.
        //What we need to know is whether an exception occurred or not

        var v = getValueMethod();
        return v != null;
    }
    catch (RuntimeBinderException)
    {
        return false;
    }
    catch
    {
        return true;
    }
}
另一种方式是:

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;
            }
        }
    }
}

以防万一,它会帮助某人:

如果方法
getDataThatLooksVerysimilarbutthesame()
返回一个
ExpandooObject
,那么在检查之前,您也可以强制转换到
IDictionary

dynamic test = new System.Dynamic.ExpandoObject();
test.foo = "bar";

if (((IDictionary<string, object>)test).ContainsKey("foo"))
{
    Console.WriteLine(test.foo);
}
dynamic test=new System.dynamic.ExpandoObject();
test.foo=“bar”;
if(((IDictionary)test).ContainsKey(“foo”))
{
Console.WriteLine(test.foo);
}

丹尼斯的回答让我想到了另一个使用JSONObject的解决方案

标题属性检查器:

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

根据@karask的回答,您可以将函数包装为助手,如下所示:

public static bool HasProperty(ExpandoObject expandoObj,
                               string name)
{
    return ((IDictionary<string, object>)expandoObj).ContainsKey(name);
}
public static bool HasProperty(ExpandoObject expandoObj,
字符串名称)
{
return((IDictionary)expandoObj).ContainsKey(name);
}

两种常见的解决方案包括发出调用并捕获
RuntimeBinderException
,使用反射检查调用,或序列化为文本格式并从中进行解析。异常的问题是它们的速度非常慢,因为当构造一个异常时,当前的调用堆栈是序列化的。序列化为JSON或类似的东西会招致类似的惩罚。这给我们留下了反射,但它只在底层对象实际上是一个具有真实成员的POCO时才起作用。如果它是围绕字典、COM对象或外部web服务的动态包装器,那么反射将不会有帮助

另一种解决方案是使用
DynamicMetaObject
获取DLR看到的成员名称。在下面的示例中,我使用一个静态类(
Dynamic
)来测试
Age
字段并显示它

class Program
{
    static void Main()
    {
        dynamic x = new ExpandoObject();

        x.Name = "Damian Powell";
        x.Age = "21 (probably)";

        if (Dynamic.HasMember(x, "Age"))
        {
            Console.WriteLine("Age={0}", x.Age);
        }
    }
}

public static class Dynamic
{
    public static bool HasMember(object dynObj, string memberName)
    {
        return GetMemberNames(dynObj).Contains(memberName);
    }

    public static IEnumerable<string> GetMemberNames(object dynObj)
    {
        var metaObjProvider = dynObj as IDynamicMetaObjectProvider;

        if (null == metaObjProvider) throw new InvalidOperationException(
            "The supplied object must be a dynamic object " +
            "(i.e. it must implement IDynamicMetaObjectProvider)"
        );

        var metaObj = metaObjProvider.GetMetaObject(
            Expression.Constant(metaObjProvider)
        );

        var memberNames = metaObj.GetDynamicMemberNames();

        return memberNames;
    }
}
类程序
{
静态void Main()
{
动态x=新的ExpandooObject();
x、 Name=“达米安·鲍威尔”;
x、 年龄=“21(可能)”;
if(Dynamic.HasMember(x,“年龄”))
{
WriteLine(“Age={0}”,x.Age);
}
}
}
公共静态类动态
{
公共静态bool HasMember(对象dynObj,字符串memberName)
{
返回GetMemberNames(dynObj).Contains(memberName);
}
公共静态IEnumerable GetMemberNames(对象dynObj)
{
var metaObjProvider=dynObj作为IDynamicMetaObjectProvider;
如果(null==metaObjProvider)抛出新的InvalidOperationException(
“提供的对象必须是动态对象”+
“(即,它必须实现IDynamicMetaObjectProvider)”
);
var metaObj=metaObjProvider.GetMetaObject(
常量表达式(metaObjProvider)
);
var memberNames=metaObj.GetDynamicMemberNames();
返回成员名称;
}
}

在我的例子中,我需要检查是否存在具有特定名称的方法,因此我使用了一个接口

var plugin = this.pluginFinder.GetPluginIfInstalled<IPlugin>(pluginName) as dynamic;
if (plugin != null && plugin is ICustomPluginAction)
{
    plugin.CustomPluginAction(action);
}
var plugin=this.pluginFinder.getpluginfinstalled(pluginName)作为动态;
if(plugin!=null&&plugin为ICustomPluginAction)
{
plugin.CustomPluginAction(action);
}
此外,接口可以包含的不仅仅是方法:

接口可以包含方法、属性、事件、索引器或任何
dynamic json = JsonConvert.DeserializeObject(data);
string header = hasHeader(json) ? json.header : null;
public static bool HasProperty(ExpandoObject expandoObj,
                               string name)
{
    return ((IDictionary<string, object>)expandoObj).ContainsKey(name);
}
class Program
{
    static void Main()
    {
        dynamic x = new ExpandoObject();

        x.Name = "Damian Powell";
        x.Age = "21 (probably)";

        if (Dynamic.HasMember(x, "Age"))
        {
            Console.WriteLine("Age={0}", x.Age);
        }
    }
}

public static class Dynamic
{
    public static bool HasMember(object dynObj, string memberName)
    {
        return GetMemberNames(dynObj).Contains(memberName);
    }

    public static IEnumerable<string> GetMemberNames(object dynObj)
    {
        var metaObjProvider = dynObj as IDynamicMetaObjectProvider;

        if (null == metaObjProvider) throw new InvalidOperationException(
            "The supplied object must be a dynamic object " +
            "(i.e. it must implement IDynamicMetaObjectProvider)"
        );

        var metaObj = metaObjProvider.GetMetaObject(
            Expression.Constant(metaObjProvider)
        );

        var memberNames = metaObj.GetDynamicMemberNames();

        return memberNames;
    }
}
var plugin = this.pluginFinder.GetPluginIfInstalled<IPlugin>(pluginName) as dynamic;
if (plugin != null && plugin is ICustomPluginAction)
{
    plugin.CustomPluginAction(action);
}
public static class DynamicHelper
{
    private static void Test( )
    {
        dynamic myobj = new
                        {
                            myInt = 1,
                            myArray = new[ ]
                                      {
                                          1, 2.3
                                      },
                            myDict = new
                                     {
                                         myInt = 1
                                     }
                        };

        var myIntOrZero = myobj.GetAsOrDefault< int >( ( Func< int > )( ( ) => myobj.noExist ) );
        int? myNullableInt = GetAs< int >( myobj, ( Func< int > )( ( ) => myobj.myInt ) );

        if( default( int ) != myIntOrZero )
            Console.WriteLine( $"myInt: '{myIntOrZero}'" );

        if( default( int? ) != myNullableInt )
            Console.WriteLine( $"myInt: '{myNullableInt}'" );

        if( DoesPropertyExist( myobj, "myInt" ) )
            Console.WriteLine( $"myInt exists and it is: '{( int )myobj.myInt}'" );
    }

    public static bool DoesPropertyExist( dynamic dyn, string property )
    {
        var t = ( Type )dyn.GetType( );
        var props = t.GetProperties( );
        return props.Any( p => p.Name.Equals( property ) );
    }

    public static object GetAs< T >( dynamic obj, Func< T > lookup )
    {
        try
        {
            var val = lookup( );
            return ( T )val;
        }
        catch( RuntimeBinderException ) { }

        return null;
    }

    public static T GetAsOrDefault< T >( this object obj, Func< T > test )
    {
        try
        {
            var val = test( );
            return ( T )val;
        }
        catch( RuntimeBinderException ) { }

        return default( T );
    }
}
dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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