IT story

C #에서 익명 유형의 속성에 액세스하는 방법은 무엇입니까?

hot-time 2020. 8. 4. 22:50
반응형

C #에서 익명 유형의 속성에 액세스하는 방법은 무엇입니까?


나는 이것을 가지고있다:

List<object> nodes = new List<object>(); 

nodes.Add(
new {
    Checked     = false,
    depth       = 1,
    id          = "div_" + d.Id
});

... 그리고 익명 객체의 "Checked"속성을 가져올 수 있는지 궁금합니다. 이것이 가능한지 확실하지 않습니다. 이것을 시도했다 :

if (nodes.Any(n => n["Checked"] == false)) ...하지만 작동하지 않습니다.

감사


강력한 유형의 익명 유형 목록을 원하면 목록을 익명 유형으로 만들어야합니다. 가장 쉬운 방법은 배열과 같은 시퀀스를 목록에 투영하는 것입니다.

var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();

그러면 다음과 같이 액세스 할 수 있습니다.

nodes.Any(n => n.Checked);

컴파일러의 작동 방식으로 인해 익명 유형이 동일한 구조를 갖기 때문에 동일한 유형이기 때문에 목록을 작성한 후에는 다음도 작동해야합니다. 그래도 이것을 확인할 수있는 컴파일러가 없습니다.

nodes.Add(new { Checked = false, /* etc */ });

객체를 type으로 저장하는 object경우 리플렉션을 사용해야합니다. 익명 또는 기타의 모든 개체 유형에 적용됩니다. 객체 o에서 다음과 같은 유형을 얻을 수 있습니다.

Type t = o.GetType();

그런 다음 속성을 찾습니다.

PropertyInfo p = t.GetProperty("Foo");

그런 다음 그 값을 얻을 수 있습니다.

object v = p.GetValue(o, null);

이 답변은 C # 4에 대한 업데이트가 오래되었습니다.

dynamic d = o;
object v = d.Foo;

이제 C # 6의 또 다른 대안 :

object v = o?.GetType().GetProperty("Foo")?.GetValue(o, null);

를 사용 ?.하면 결과 vnull세 가지 다른 상황에있게됩니다!

  1. o이다 null, 그래서 객체는 전혀 없다
  2. o비- null하지만 속성이 없습니다Foo
  3. o속성이 Foo있지만 실제 값은입니다 null.

따라서 이것은 이전 예제와 동일하지 않지만 세 가지 경우를 모두 동일하게 취급하려는 경우 이치에 맞습니다.


Reflection을 사용하여 익명 형식의 속성을 반복 할 수 있습니다. "Checked"속성이 있는지 확인하고 값이 있으면 확인하십시오.

이 블로그 게시물 참조 : http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx

그래서 같은 :

foreach(object o in nodes)
{
    Type t = o.GetType();

    PropertyInfo[] pi = t.GetProperties(); 

    foreach (PropertyInfo p in pi)
    {
        if (p.Name=="Checked" && !(bool)p.GetValue(o))
            Console.WriteLine("awesome!");
    }
}

최근에 .NET 3.5에서 동일한 문제가 발생했습니다 (동적 사용 가능하지 않음). 내가 해결 한 방법은 다음과 같습니다.

// pass anonymous object as argument
var args = new { Title = "Find", Type = typeof(FindCondition) };

using (frmFind f = new frmFind(args)) 
{
...
...
}

스택 오버 플로우의 어딘가에서 적응 :

// Use a custom cast extension
public static T CastTo<T>(this Object x, T targetType)
{
   return (T)x;
}

이제 캐스트를 통해 객체를 다시 가져옵니다.

public partial class frmFind: Form
{
    public frmFind(object arguments)
    {

        InitializeComponent();

        var args = arguments.CastTo(new { Title = "", Type = typeof(Nullable) });

        this.Text = args.Title;

        ...
    }
    ...
}

수락 된 답변은 목록을 어떻게 선언해야하는지 정확하게 설명하며 대부분의 시나리오에 권장됩니다.

그러나 나는 다른 시나리오를 보았습니다.이 시나리오는 또한 질문 된 질문을 다룹니다. ViewData["htmlAttributes"]MVC 에서와 같이 기존 객체 목록을 사용해야하는 경우 어떻게 합니까? 속성에 어떻게 액세스 할 수 new { @style="width: 100px", ... }있습니까 ( 보통을 통해 생성됨 )?

For this slightly different scenario I want to share with you what I found out. In the solutions below, I am assuming the following declaration for nodes:

List<object> nodes = new List<object>();

nodes.Add(
new
{
    Checked = false,
    depth = 1,
    id = "div_1" 
});

1. Solution with dynamic

In C# 4.0 and higher versions, you can simply cast to dynamic and write:

if (nodes.Any(n => ((dynamic)n).Checked == false))
    Console.WriteLine("found not checked element!");

Note: This is using late binding, which means it will recognize only at runtime if the object doesn't have a Checked property and throws a RuntimeBinderException in this case - so if you try to use a non-existing Checked2 property you would get the following message at runtime: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'".

2. Solution with reflection

The solution with reflection works both with old and new C# compiler versions. For old C# versions please regard the hint at the end of this answer.

Background

As a starting point, I found a good answer here. The idea is to convert the anonymous data type into a dictionary by using reflection. The dictionary makes it easy to access the properties, since their names are stored as keys (you can access them like myDict["myProperty"]).

Inspired by the code in the link above, I created an extension class providing GetProp, UnanonymizeProperties and UnanonymizeListItems as extension methods, which simplify access to anonymous properties. With this class you can simply do the query as follows:

if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
    Console.WriteLine("found not checked element!");
}

or you can use the expression nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any() as if condition, which filters implicitly and then checks if there are any elements returned.

To get the first object containing "Checked" property and return its property "depth", you can use:

var depth = nodes.UnanonymizeListItems()
             ?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");

or shorter: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];

Note: If you have a list of objects which don't necessarily contain all properties (for example, some do not contain the "Checked" property), and you still want to build up a query based on "Checked" values, you can do this:

if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true)); 
                                      return y.HasValue && y.Value == false;}).Any())
{
    Console.WriteLine("found not checked element!");
}

This prevents, that a KeyNotFoundException occurs if the "Checked" property does not exist.


The class below contains the following extension methods:

  • UnanonymizeProperties: Is used to de-anonymize the properties contained in an object. This method uses reflection. It converts the object into a dictionary containing the properties and its values.
  • UnanonymizeListItems: Is used to convert a list of objects into a list of dictionaries containing the properties. It may optionally contain a lambda expression to filter beforehand.
  • GetProp: Is used to return a single value matching the given property name. Allows to treat not-existing properties as null values (true) rather than as KeyNotFoundException (false)

For the examples above, all that is required is that you add the extension class below:

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

Hint: The code above is using the null-conditional operators, available since C# version 6.0 - if you're working with older C# compilers (e.g. C# 3.0), simply replace ?. by . and ?[ by [ everywhere, e.g.

var depth = nodes.UnanonymizeListItems()
            .FirstOrDefault(n => n.Contains("Checked"))["depth"];

If you're not forced to use an older C# compiler, keep it as is, because using null-conditionals makes null handling much easier.

Note: Like the other solution with dynamic, this solution is also using late binding, but in this case you're not getting an exception - it will simply not find the element if you're referring to a non-existing property, as long as you keep the null-conditional operators.

What might be useful for some applications is that the property is referred to via a string in solution 2, hence it can be parameterized.

참고URL : https://stackoverflow.com/questions/1203522/how-to-access-property-of-anonymous-type-in-c

반응형