C # 'da anonim türün özelliğine nasıl erişilir?


125

Bu bende var:

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

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

... ve anonim nesnenin "Checked" özelliğini alabilir miyim diye merak ediyorum. Bunun mümkün olup olmadığından bile emin değilim. Bunu yapmaya çalıştım:

if (nodes.Any(n => n["Checked"] == false)) ... ama işe yaramıyor.

Teşekkürler

Yanıtlar:


63

Kesin yazılmış bir anonim türler listesi istiyorsanız, listeyi de anonim bir tür yapmanız gerekir. Bunu yapmanın en kolay yolu, dizi gibi bir diziyi bir listeye yansıtmaktır, ör.

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

Ardından aşağıdaki gibi erişebileceksiniz:

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

Derleyicinin çalışma şekli nedeniyle, listeyi oluşturduktan sonra aşağıdakiler de çalışmalıdır, çünkü anonim türler aynı yapıya sahip olduğundan bunlar da aynı türdedir. Yine de bunu doğrulayacak bir derleyicim yok.

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

263

Nesneyi tür olarak depoluyorsanız, objectyansımayı kullanmanız gerekir. Bu, anonim veya başka herhangi bir nesne türü için geçerlidir. Bir obje üzerinde o tipini alabilirsiniz:

Type t = o.GetType();

Bundan sonra bir mülke bakarsınız:

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

Sonra bundan bir değer elde edebilirsiniz:

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

Bu yanıt, C # 4 için bir güncelleme için çok gecikti:

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

Ve şimdi C # 6'da başka bir alternatif:

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

Not kullanarak bu ?.biz neden sonuçlanan volmak nullüç farklı durumlarda!

  1. oolduğunu null, bu nedenle hiçbir nesne hiç yoktur
  2. oolmayan bir nullama bir özellik yokFoo
  3. obir özelliği vardır, Fooancak gerçek değeri olur null.

Yani bu önceki örneklerle eşdeğer değildir, ancak üç vakayı da aynı şekilde ele almak istiyorsanız mantıklı olabilir.


4
Şimdiye kadar hiç dinamik kullanmadım, .NET 4.0 için güzel güncelleme
Alan

c # 4 çözümünde, özellik yoksa çalışma zamanı istisnası alırsınız ( object v = d.Foo), GetValue(o, null)yoksa boş olacaktır.
YaakovHatam

1
Hayır, GetPropertygeri dönecek nullve GetValuebunu geçerse atacaktır null, bu nedenle genel etki bir istisnadır. C # 4.0 sürümü daha açıklayıcı bir istisna verir.
Daniel Earwicker

4
Dinamik olarak kaynaktan farklı bir montajda kullanıyorsanız, [InternalsVisibleTo]
Sarath

2
@DanielEarwicker tamamladığınız için teşekkürler. Anonim tipler için de geçerlidir. Anonim türler için oluşturulan tüm özellikler dahili olduğundan.
Sarath

13

Reflection kullanarak anonim türün özelliklerini yineleyebilirsiniz; "Kontrol Edilmiş" bir özellik olup olmadığına bakın ve varsa değerini alın.

Bu blog gönderisine bakın: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx

Yani şöyle bir şey:

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!");
    }
}

6
Yalnızca bir mülke ihtiyacınız varsa ve adını zaten biliyorsanız, hepsini gözden geçirmenin bir anlamı yoktur; sadece GetProperty ve GetValue kullanın. Ayrıca, System.out.println Java'dır, C # değildir ...
Chris Charabaruk

Oops, öyleyse, Chris! Biraz utanç verici ... şimdi düzeltildi.
glennkentwell

6

Kabul edilen cevap, listenin nasıl açıklanması gerektiğini doğru bir şekilde açıklar ve çoğu senaryo için şiddetle tavsiye edilir.

Ama sorulan soruyu da kapsayan farklı bir senaryo ile karşılaştım. Eğer gibi, varolan nesne listesini kullanmak için ne varsa ViewData["htmlAttributes"]içinde MVC ? Özelliklerine nasıl erişebilirsiniz (genellikle aracılığıyla oluşturulurlar new { @style="width: 100px", ... })?

Bu biraz farklı senaryo için öğrendiklerimi sizinle paylaşmak istiyorum. Aşağıdaki çözümlerde, aşağıdaki beyanı varsayıyorum nodes:

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

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

1. Dinamik çözüm

İçinde C # 4.0 ve daha yüksek sürümleri, sadece dinamik ve yazma yayın yapabilirsiniz:

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

Not: Bu, geç bağlamayı kullanıyor , yani yalnızca çalışma zamanında nesnenin bir Checkedözelliği yoksa ve birRuntimeBinderException bu durumda - bu nedenle, mevcut olmayan bir Checked2özelliği kullanmaya çalışırsanız şu mesajı alırsınız : Çalışma zamanı: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'" .

2. Yansımalı çözüm

Yansımalı çözüm işe yarıyor hem eski hem de yeni C # derleyici sürümleriyle çalışır. Eski C # sürümleri için lütfen bu cevabın sonundaki ipucunu dikkate alın.

Arka fon

Başlangıç ​​noktası olarak burada iyi bir cevap buldum . Buradaki fikir, anonim veri türünü yansıma kullanarak bir sözlüğe dönüştürmektir. Sözlük, isimleri anahtar olarak saklandığından (istediğiniz gibi erişebilirsiniz myDict["myProperty"]) özelliklere erişimi kolaylaştırır .

Yukarıdaki linkte kod esinlenerek, ben sağlayan bir uzatma sınıfını yarattı GetProp, UnanonymizePropertiesve UnanonymizeListItemsuzatma yöntemleri anonim özelliklerine basitleştirmek erişim gibi. Bu sınıfla, sorguyu aşağıdaki gibi yapabilirsiniz:

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

veya ifadeyi koşul nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()olarak kullanabilirsiniz; ifbu, örtük olarak filtreler ve sonra döndürülen herhangi bir öğe olup olmadığını kontrol eder.

"Kontrol Edildi" özelliğini içeren ilk nesneyi almak ve "derinlik" özelliğini döndürmek için şunları kullanabilirsiniz:

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

veya daha kısa: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];

Not: Tüm özellikleri içermesi gerekmeyen nesnelerin bir listeniz varsa (örneğin, bazıları "Kontrol edilen" özelliği içermiyorsa) ve yine de "Kontrol edilen" değerlere dayalı bir sorgu oluşturmak istiyorsanız, Bunu yap:

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

Bu KeyNotFoundException, "Kontrol Edildi" özelliği yoksa a oluşmasını engeller .


Aşağıdaki sınıf, aşağıdaki uzatma yöntemlerini içerir:

  • UnanonymizeProperties: Bir nesnede bulunan özelliklerin anonim hale getirilmesi için kullanılır . Bu yöntem yansıma kullanır. Nesneyi, özellikleri ve değerlerini içeren bir sözlüğe dönüştürür.
  • UnanonymizeListItems: Nesnelerin bir listesini, özellikleri içeren bir sözlükler listesine dönüştürmek için kullanılır. Önceden filtrelemek için isteğe bağlı olarak bir lambda ifadesi içerebilir .
  • GetProp: Verilen özellik adıyla eşleşen tek bir değer döndürmek için kullanılır. Var olmayan özellikleri KeyNotFoundException (false) yerine boş değerler (doğru) olarak değerlendirmeye izin verir

Yukarıdaki örnekler için gereken tek şey, aşağıdaki uzantı sınıfını eklemenizdir:

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

İpucu: Yukarıdaki kod kullanarak boş-koşullu C # sürüm 6.0 beri mevcut operatörler, - eğer Birlikte olduğun çalışma eski C # derleyicileri (örneğin C # 3.0), basitçe yerine ?.göre .ve ?[tarafından [örn her yerde

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

Eğer ediyorsanız değil eski bir C # derleyicisi kullanmak zorunda boş-conditionals kullanarak çok daha kolay taşıma null adlı yapar, çünkü o kadar olduğunu tutun.

Not: Dinamikli diğer çözüm gibi, bu çözüm de geç bağlama kullanıyor, ancak bu durumda bir istisna almıyorsunuz - mevcut olmayan bir mülke atıfta bulunuyorsanız öğeyi bulamayacağı sürece boş koşullu tuttuğunuzda operatörleri.

Bazı uygulamalar için faydalı olabilecek şey, özelliğe çözüm 2'de bir dizge aracılığıyla başvurulması, dolayısıyla parametreleştirilebilmesidir.


1

Son zamanlarda, .NET 3.5'te aynı sorunu yaşadım (dinamik yok). İşte böyle çözdüm:

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

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

Stackoverflow'da bir yerden uyarlanmıştır:

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

Şimdi nesneyi cast yoluyla geri alın:

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

        InitializeComponent();

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

        this.Text = args.Title;

        ...
    }
    ...
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.