답변:
강력한 형식의 익명 형식 목록이 필요한 경우 목록도 익명 형식으로 만들어야합니다. 가장 쉬운 방법은 배열과 같은 시퀀스를 목록에 투영하는 것입니다.
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);
를 사용 ?.
하면 결과 v
가 null
세 가지 다른 상황에있게됩니다!
o
이다 null
, 그래서 객체는 전혀 없다o
는 null
아니지만 속성이 없습니다.Foo
o
속성이 Foo
있지만 실제 값은입니다 null
.따라서 이것은 이전 예제와 동일하지 않지만 세 가지 경우를 모두 동일하게 처리하려는 경우 의미가있을 수 있습니다.
object v = d.Foo
) 런타임 예외가 발생하고 존재 GetValue(o, null)
하지 않으면 null이됩니다.
GetProperty
을 반환 null
하고 GetValue
전달하면 throw null
되므로 전반적인 효과는 예외입니다. C # 4.0 버전은보다 설명적인 예외를 제공합니다.
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!");
}
}
허용되는 답변은 목록을 선언하는 방법을 올바르게 설명하며 대부분의 시나리오에 적극 권장됩니다.
그러나 나는 질문을 다루는 다른 시나리오를 발견했습니다. 당신은 같은 기존 개체 목록을 사용하려면 어떻게이있는 경우 ViewData["htmlAttributes"]
에 MVC ? 속성에 어떻게 액세스 할 수 new { @style="width: 100px", ... }
있습니까 ( 일반적으로를 통해 생성됨 )?
이 약간 다른 시나리오를 위해 제가 알아 낸 것을 여러분과 공유하고 싶습니다. 아래 솔루션에서 다음 선언을 가정합니다 nodes
.
List<object> nodes = new List<object>();
nodes.Add(
new
{
Checked = false,
depth = 1,
id = "div_1"
});
에서 C # 4.0 이상 버전, 당신은 단순히 동적 및 쓰기로 캐스팅 할 수 있습니다 :
if (nodes.Any(n => ((dynamic)n).Checked == false))
Console.WriteLine("found not checked element!");
참고 : 이것은 후기 바인딩을 사용 합니다. 즉, 객체에 Checked
속성 이없는 경우 런타임에만 인식 RuntimeBinderException
하고이 경우 a 를 throw합니다 . 따라서 존재 하지 않는 속성 을 사용하려고 Checked2
하면 다음 메시지가 표시 됩니다. 런타임 : "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.
리플렉션이있는 솔루션은 이전 및 새 C # 컴파일러 버전 모두에서 작동 합니다 . 이전 C # 버전의 경우이 답변 끝에있는 힌트를 참조하십시오.
배경
시작점으로 여기 에서 좋은 답을 찾았 습니다 . 아이디어는 리플렉션을 사용하여 익명 데이터 유형을 사전으로 변환하는 것입니다. 사전을 사용하면 속성 이름이 키로 저장되므로 속성에 쉽게 액세스 할 수 있습니다 (처럼 액세스 할 수 있음 myDict["myProperty"]
).
위의 링크에서 코드에 의해 영감을, 내가 제공하는 확장 클래스를 생성 GetProp
, UnanonymizeProperties
및 UnanonymizeListItems
확장 방법, 익명의 속성을 단순화 액세스있다. 이 클래스를 사용하면 다음과 같이 간단히 쿼리를 수행 할 수 있습니다.
if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
Console.WriteLine("found not checked element!");
}
또는 암시 적으로 필터링 한 다음 반환 된 요소가 있는지 확인하는 조건으로 표현식 nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
을 사용할 수 있습니다 if
.
"Checked"속성을 포함하는 첫 번째 개체를 가져오고 속성 "depth"를 반환하려면 다음을 사용할 수 있습니다.
var depth = nodes.UnanonymizeListItems()
?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
이하 : nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
참고 : 모든 속성을 포함 할 필요는없는 개체 목록이 있고 (예 : 일부는 "Checked"속성을 포함하지 않음) "Checked"값을 기반으로 쿼리를 작성하려는 경우 다음을 수행 할 수 있습니다. 이 작업을 수행:
if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true));
return y.HasValue && y.Value == false;}).Any())
{
Console.WriteLine("found not checked element!");
}
이것은 KeyNotFoundException
"Checked"속성이 존재하지 않는 경우 발생 하는 것을 방지 합니다.
아래 클래스에는 다음 확장 메서드가 포함되어 있습니다.
UnanonymizeProperties
: 개체에 포함 된 속성의 익명화 를 해제 하는 데 사용됩니다 . 이 방법은 반사를 사용합니다. 객체를 속성과 해당 값을 포함하는 사전으로 변환합니다.UnanonymizeListItems
: 객체 목록을 속성이 포함 된 사전 목록으로 변환하는 데 사용됩니다. 미리 필터링 할 람다 식을 선택적으로 포함 할 수 있습니다 .GetProp
: 주어진 속성 이름과 일치하는 단일 값을 반환하는 데 사용됩니다. 존재하지 않는 속성을 KeyNotFoundException (false)이 아닌 null 값 (true)으로 처리 할 수 있습니다.위의 예에서 필요한 것은 아래 확장 클래스를 추가하는 것입니다.
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;
}
}
}
힌트 : 위의 코드는 사용 널 조건부 C # 버전 6.0 이후 사용 가능한 연산자를 - 만약 당신이 나왔습니다 작업 이전의 C # 컴파일러 (예를 들어, C # 3.0), 간단하게 교체 ?.
가 .
와 ?[
에 의해 [
예를 들어, 모든 곳
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
이전 C # 컴파일러를 강제로 사용 하지 않는 경우 null 조건을 사용하면 null 처리가 훨씬 쉬워 지므로 그대로 유지하세요.
참고 : 동적을 사용하는 다른 솔루션과 마찬가지로이 솔루션도 후기 바인딩을 사용하지만이 경우 예외가 발생하지 않습니다. 존재하지 않는 속성을 참조하는 경우 널 조건부 연산자 를 유지하면 됩니다.
일부 애플리케이션에 유용 할 수있는 것은 속성이 솔루션 2의 문자열을 통해 참조되므로 매개 변수화 할 수 있다는 것입니다.
최근에 .NET 3.5에서 동일한 문제가 발생했습니다 (동적 사용 불가). 내가 해결 한 방법은 다음과 같습니다.
// pass anonymous object as argument
var args = new { Title = "Find", Type = typeof(FindCondition) };
using (frmFind f = new frmFind(args))
{
...
...
}
stackoverflow 어딘가에서 수정되었습니다.
// 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;
...
}
...
}