Programing

동적 변수에서 속성을 사용할 수 있는지 테스트

lottogame 2020. 4. 30. 07:27
반응형

동적 변수에서 속성을 사용할 수 있는지 테스트


내 상황은 매우 간단합니다. 내 코드 어딘가에 나는 이것을 가지고있다 :

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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

따라서 기본적으로 내 질문은 특정 속성이 내 동적 변수에서 사용할 수 있는지 확인하는 방법입니다 (예외 발생). 나는 할 수 GetType()있지만 객체의 유형을 알 필요가 없기 때문에 오히려 피하고 싶습니다. 내가 정말로 알고 싶은 것은 재산 (또는 삶을 더 쉽게 만드는 방법)이 있는지 여부입니다. 어떤 포인터?


dynamicC # 컴파일러에서 동적 바인딩이 처리되는 방식을 다시 구현하지 않으면 변수에 액세스하지 않고 특정 멤버가 있는지 여부를 알 수있는 방법이 없다고 생각합니다 . C # 사양에 따라 구현 정의되어 있기 때문에 많은 추측이 포함될 것입니다.

따라서 실제로 멤버에 액세스하고 실패하면 예외를 포착해야합니다.

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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

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의 의견에 답변 :

비율은 reflection:exception100000 회 반복에 대한 틱입니다.

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()사전 또는 이와 유사한 방식으로 각 유형 의 결과를 캐싱하여 프로세스 속도를 높일 수 있습니다 . 동일한 유형의 집합을 반복해서 확인하는 경우 도움이 될 수 있습니다.


아마도 반사를 사용합니까?

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

누군가를 돕는 경우를 대비하여 :

메소드가를 GetDataThatLooksVerySimilarButNotTheSame()반환하면 확인하기 전에 ExpandoObject캐스팅 할 수도 있습니다 IDictionary.

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

if (((IDictionary<string, object>)test).ContainsKey("foo"))
{
    Console.WriteLine(test.foo);
}

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;

이에 대한 두 가지 일반적인 솔루션에는 전화 걸기 및 받기, RuntimeBinderException리플렉션을 사용하여 전화 확인 또는 텍스트 형식으로 직렬화 및 구문 분석이 포함됩니다. 예외의 문제점은 하나가 구성 될 때 현재 호출 스택이 직렬화되기 때문에 매우 느리다는 것입니다. JSON 또는 유사한 것으로 직렬화하면 비슷한 형벌이 발생합니다. 이것은 우리에게 반성을 남기지 만 기본 객체가 실제로 실제 멤버가있는 POCO 인 경우에만 작동합니다. 사전, COM 개체 또는 외부 웹 서비스 주변의 동적 래퍼 인 경우 리플렉션이 도움이되지 않습니다.

또 다른 해결책 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;
    }
}

글쎄, 나는 비슷한 문제에 직면했지만 단위 테스트에 직면했다.

SharpTestsEx를 사용하면 속성이 존재하는지 확인할 수 있습니다. JSON 객체가 동적이기 때문에 누군가가 이름을 변경하고 자바 스크립트 등에서 이름을 변경하는 것을 잊어 버릴 수 있으므로이 컨트롤러 테스트를 사용합니다. 따라서 컨트롤러를 작성할 때 모든 속성을 테스트하면 안전성이 높아집니다.

예:

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 ()"를 사용하여 기존의 모든 속성을 테스트합니다.

아마도 주제가 아닐 수도 있지만 누군가에게 유용 할 수 있습니다.


@karask의 답변에서 다음과 같이 함수를 도우미로 래핑 할 수 있습니다.

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

나를 위해 이것은 작동합니다 :

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

동적으로 사용되는 유형을 제어하면 모든 속성 액세스에 대한 값 대신 튜플을 반환 할 수 없습니까? 뭔가 ...

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

    T Value { get; private set; }
    bool Exists { get; private set; }
}

Possibly a naive implementation, but if you construct one of these internally each time and return that instead of the actual value, you can check Exists on every property access and then hit Value if it does with value being default(T) (and irrelevant) if it doesn't.

That said, I might be missing some knowledge on how dynamic works and this might not be a workable suggestion.


In my case, I needed to check for the existence of a method with a specific name, so I used an interface for that

var plugin = this.pluginFinder.GetPluginIfInstalled<IPlugin>(pluginName) as dynamic;
if (plugin != null && plugin is ICustomPluginAction)
{
    plugin.CustomPluginAction(action);
}

Also, interfaces can contain more than just methods:

Interfaces can contain methods, properties, events, indexers, or any combination of those four member types.

From: Interfaces (C# Programming Guide)

Elegant and no need to trap exceptions or play with reflexion...


I know this is really old post but here is a simple solution to work with dynamic type in c#.

  1. can use simple reflection to enumerate direct properties
  2. or can use the object extention method
  3. or use GetAsOrDefault<int> method to get a new strongly typed object with value if exists or default if not exists.
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 );
    }
}

Here is the other way:

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

참고URL : https://stackoverflow.com/questions/2998954/test-if-a-property-is-available-on-a-dynamic-variable

반응형