c # foreach (คุณสมบัติในวัตถุ) …มีวิธีง่ายๆในการทำเช่นนี้หรือไม่?


92

ฉันมีคลาสที่มีคุณสมบัติหลายอย่าง (ทั้งหมดเป็นสตริงหากมีความแตกต่าง)
ฉันยังมีรายการซึ่งมีอินสแตนซ์ต่างๆของคลาส

ในขณะที่สร้างการทดสอบหน่วยสำหรับชั้นเรียนของฉันฉันตัดสินใจว่าฉันต้องการวนซ้ำแต่ละวัตถุในรายการจากนั้นวนซ้ำคุณสมบัติแต่ละรายการของวัตถุนั้น ...

ฉันคิดว่าการทำเช่นนี้จะง่ายเหมือน ...

foreach (Object obj in theList)
{
     foreach (Property theProperties in obj)
     {
         do some stufff!!;
     }
}

แต่ไม่ได้ผล! :( ฉันได้รับข้อผิดพลาดนี้ ...

"คำสั่ง foreach ไม่สามารถทำงานกับตัวแปรประเภท 'Application.Object' ได้เนื่องจาก 'Application.Object' ไม่มีคำจำกัดความสาธารณะสำหรับ 'GetEnumerator'"

ไม่มีใครรู้วิธีการทำสิ่งนี้โดยไม่ต้องใช้ ifs และ loops มากมายหรือไม่ต้องเข้าไปทำอะไรที่ซับซ้อนเกินไป?


5
ในอนาคตโปรดอย่าพูดว่า "ใช้ไม่ได้" ในคำถาม ให้ระบุปัญหาที่คุณประสบแทน (ข้อผิดพลาดของคอมไพเลอร์ ฯลฯ ) ขอบคุณ!
Robert Harvey

2
อัพเดท! ขอบคุณสำหรับการ
แจ้งเตือน

คำตอบ:


143

ลองดูสิ:

foreach (PropertyInfo propertyInfo in obj.GetType().GetProperties())
{
   // do stuff here
}

นอกจากนี้โปรดทราบว่าType.GetProperties()มีโอเวอร์โหลดซึ่งยอมรับชุดของแฟล็กการผูกเพื่อให้คุณสามารถกรองคุณสมบัติในเกณฑ์ต่างๆเช่นระดับการช่วยสำหรับการเข้าถึงดู MSDN สำหรับรายละเอียดเพิ่มเติม: Type.GetProperties Method (BindingFlags)สุดท้าย แต่ไม่ท้ายสุดอย่าลืม เพิ่มการอ้างอิงการประกอบ "system.Reflection"

ตัวอย่างเช่นเพื่อแก้ไขคุณสมบัติสาธารณะทั้งหมด:

foreach (var propertyInfo in obj.GetType()
                                .GetProperties(
                                        BindingFlags.Public 
                                        | BindingFlags.Instance))
{
   // do stuff here
}

โปรดแจ้งให้เราทราบว่าจะได้ผลตามที่คาดไว้หรือไม่


4
เมื่อคุณจัดการกับคุณสมบัติของวัตถุได้แล้วจะมีการคว้ามูลค่าของคุณสมบัติแต่ละอย่างหรือไม่? เช่นชื่อหรือรหัสไปรษณีย์เช่น
JsonStatham

36

คุณสามารถวนลูปผ่านคุณสมบัติที่ไม่ได้จัดทำดัชนีทั้งหมดของอ็อบเจ็กต์ดังนี้:

var s = new MyObject();
foreach (var p in s.GetType().GetProperties().Where(p => !p.GetGetMethod().GetParameters().Any())) {
    Console.WriteLine(p.GetValue(s, null));
}

เนื่องจากGetProperties()ส่งคืนตัวสร้างดัชนีเช่นเดียวกับคุณสมบัติทั่วไปคุณจึงต้องมีตัวกรองเพิ่มเติมก่อนที่จะโทรGetValueเพื่อให้ทราบว่าปลอดภัยที่จะส่งnullเป็นพารามิเตอร์ที่สอง

คุณอาจต้องแก้ไขตัวกรองเพิ่มเติมเพื่อกำจัดคุณสมบัติแบบเขียนอย่างเดียวและอื่น ๆ ที่ไม่สามารถเข้าถึงได้


+1 สำหรับการทำบางอย่างกับคุณสมบัติที่สามารถใช้ได้จริงเท่านั้นคุณอาจต้องการกรองคุณสมบัติแบบเขียนอย่างเดียวออกไปด้วย

@hvd เป็นจุดที่ยอดเยี่ยมในคุณสมบัติการเขียนเท่านั้น! ฉันเกือบลืมไปแล้ว รหัสของฉันจะผิดพลาดหากพบกับคุณสมบัติที่มีnullgetter แต่ฉันแน่ใจว่า OP จะหาวิธีรับเฉพาะคุณสมบัติที่เขาต้องการ
Sergey Kalinichenko

27

เกือบจะถึงแล้วคุณเพียงแค่ต้องได้รับคุณสมบัติจากประเภทแทนที่จะคาดหวังว่าคุณสมบัติจะสามารถเข้าถึงได้ในรูปแบบของคอลเลกชันหรือกระเป๋าทรัพย์สิน:

var property in obj.GetType().GetProperties()

จากที่นั่นคุณสามารถเข้าถึงได้ดังนี้:

property.Name
property.GetValue(obj, null)

ด้วยGetValueพารามิเตอร์ที่สองจะช่วยให้คุณระบุค่าดัชนีซึ่งจะทำงานร่วมกับคุณสมบัติที่ส่งคืนคอลเล็กชัน - เนื่องจากสตริงเป็นชุดของอักขระคุณยังสามารถระบุดัชนีเพื่อส่งคืนอักขระได้หากจำเป็น


1
ฉันทำให้ใครไม่พอใจ? ฉันเปิดใจที่จะรู้ว่ามีอะไรผิดปกติเกี่ยวกับเรื่องนี้ไม่งั้นฉันอาจจะไม่มีทางได้เรียน
Grant Thomas

คุณอาจได้รับการโหวตลงคะแนนเมื่อรหัสของคุณผิด (ก่อนที่นินจาจะแก้ไข)
Robert Harvey

20

แน่นอนไม่มีปัญหา:

foreach(object item in sequence)
{
    if (item == null) continue;
    foreach(PropertyInfo property in item.GetType().GetProperties())
    {
        // do something with the property
    }
}

ทำไมคุณถึงเพิ่มบรรทัดนี้ if (item == null) continue;โดยส่วนตัวฉันคิดว่าถ้าคุณมีวัตถุว่าง ณ จุดนั้นมีบางอย่างผิดพลาดก่อนหน้านี้มากและนั่นคือจุดที่การตรวจสอบความถูกต้องหรือฉันผิด?
Rafael Herscovici

@ สมองเสื่อม: แน่นอนเราสามารถพูดได้ว่าลำดับนั้นจำเป็นต้องมีการอ้างอิงที่ไม่ใช่ค่าว่าง
Eric Lippert

3

ใช้ Reflection เพื่อทำสิ่งนี้

SomeClass A = SomeClass(...)
PropertyInfo[] properties = A.GetType().GetProperties();

2

ฉันมองหาคำตอบสำหรับคำถามที่คล้ายกันในหน้านี้ฉันเขียนคำตอบของคำถามที่คล้ายกันหลายข้อซึ่งอาจช่วยให้ผู้ที่เข้ามาในหน้านี้

รายชื่อชั้นเรียน

รายการ <T> คลาสแสดงถึงรายการของวัตถุที่ดัชนีสามารถเข้าถึงได้ อยู่ภายใต้ System.Collection.Generic namespace คลาสรายการสามารถใช้เพื่อสร้างคอลเลกชันของประเภทต่างๆเช่นจำนวนเต็มสตริง ฯลฯ คลาสรายการยังมีวิธีการค้นหาเรียงลำดับและจัดการรายการ

ชั้นที่มีทรัพย์สิน :

class TestClss
{
    public string id { set; get; }
    public string cell1 { set; get; }
    public string cell2 { set; get; }
}
var MyArray = new List<TestClss> {
    new TestClss() { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    new TestClss() { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    new TestClss() { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data" }
};
foreach (object Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (PropertyInfo property in Item.GetType().GetProperties())
    {
        var Key = property.Name;
        var Value = property.GetValue(Item, null);
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

หรือคลาสพร้อมฟิลด์ :

class TestClss
{
    public string id = "";
    public string cell1 = "";
    public string cell2 = "";
}
var MyArray = new List<TestClss> {
    new TestClss() { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    new TestClss() { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    new TestClss() { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data" }
};
foreach (object Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (var fieldInfo in Item.GetType().GetFields())
    {
        var Key = fieldInfo.Name;
        var Value = fieldInfo.GetValue(Item);
    }

}

หรือรายการวัตถุ (ไม่มีเซลล์เดียวกัน):

var MyArray = new List<object> {
    new { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    new { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    new { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data", anotherCell = "" }
};
foreach (object Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (var props in Item.GetType().GetProperties())
    {
        var Key = props.Name;
        var Value = props.GetMethod.Invoke(Item, null).ToString();
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

หรือรายการวัตถุ (ต้องมีเซลล์เดียวกัน):

var MyArray = new[] {
    new { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    new { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    new { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data" }
};
foreach (object Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (var props in Item.GetType().GetProperties())
    {
        var Key = props.Name;
        var Value = props.GetMethod.Invoke(Item, null).ToString();
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

หรือรายการวัตถุ (พร้อมคีย์):

var MyArray = new {
    row1 = new { id = "1", cell1 = "cell 1 row 1 Data", cell2 = "cell 2 row 1 Data" },
    row2 = new { id = "2", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 2 Data" },
    row3 = new { id = "3", cell1 = "cell 1 row 2 Data", cell2 = "cell 2 row 3 Data" }
};
// using System.ComponentModel;  for TypeDescriptor
foreach (PropertyDescriptor Item in TypeDescriptor.GetProperties(MyArray))
{
    string Rowkey = Item.Name;
    object RowValue = Item.GetValue(MyArray);
    Console.WriteLine("Row key is: {0}", Rowkey);
    foreach (var props in RowValue.GetType().GetProperties())
    {
        var Key = props.Name;
        var Value = props.GetMethod.Invoke(RowValue, null).ToString();
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

หรือรายชื่อพจนานุกรม

var MyArray = new List<Dictionary<string, string>>() {
    new Dictionary<string, string>() { { "id", "1" }, { "cell1", "cell 1 row 1 Data" }, { "cell2", "cell 2 row 1 Data" } },
    new Dictionary<string, string>() { { "id", "2" }, { "cell1", "cell 1 row 2 Data" }, { "cell2", "cell 2 row 2 Data" } },
    new Dictionary<string, string>() { { "id", "3" }, { "cell1", "cell 1 row 3 Data" }, { "cell2", "cell 2 row 3 Data" } }
};
foreach (Dictionary<string, string> Item in MyArray)
{
    Console.WriteLine("Row Start");
    foreach (KeyValuePair<string, string> props in Item)
    {
        var Key = props.Key;
        var Value = props.Value;
        Console.WriteLine("{0}={1}", Key, Value);
    }
}

โชคดี..


0

คำเตือนเล็กน้อยหาก "ทำบางสิ่ง" หมายถึงการอัปเดตมูลค่าของคุณสมบัติจริงที่คุณเยี่ยมชมและหากมีคุณสมบัติประเภทโครงสร้างตามเส้นทางจากวัตถุรากไปยังคุณสมบัติที่เข้าชมการเปลี่ยนแปลงที่คุณทำกับคุณสมบัติจะ ไม่สะท้อนบนวัตถุราก


0

โซลูชันการคัดลอกวาง (วิธีการขยาย) ส่วนใหญ่อิงตามคำตอบก่อนหน้านี้สำหรับคำถามนี้

ยังจัดการ IDicitonary (ExpandoObject / dynamic) อย่างเหมาะสมซึ่งมักจำเป็นเมื่อจัดการกับสิ่งที่สะท้อนกลับนี้

ไม่แนะนำให้ใช้ในลูปแน่นและเส้นทางร้อนอื่น ๆ ในกรณีเหล่านี้คุณจะต้องใช้แคช / IL emit / การคอมไพล์ทรีนิพจน์

    public static IEnumerable<(string Name, object Value)> GetProperties(this object src)
    {
        if (src is IDictionary<string, object> dictionary)
        {
            return dictionary.Select(x => (x.Key, x.Value));
        }
        return src.GetObjectProperties().Select(x => (x.Name, x.GetValue(src)));
    }

    public static IEnumerable<PropertyInfo> GetObjectProperties(this object src)
    {
        return src.GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => !p.GetGetMethod().GetParameters().Any());
    }

-1

ฉันไม่สามารถหาวิธีทำงานใด ๆ ข้างต้นได้ แต่วิธีนี้ได้ผล ชื่อผู้ใช้และรหัสผ่านสำหรับ DirectoryEntry เป็นทางเลือก

   private List<string> getAnyDirectoryEntryPropertyValue(string userPrincipalName, string propertyToSearchFor)
    {
        List<string> returnValue = new List<string>();
        try
        {
            int index = userPrincipalName.IndexOf("@");
            string originatingServer = userPrincipalName.Remove(0, index + 1);
            string path = "LDAP://" + originatingServer; //+ @"/" + distinguishedName;
            DirectoryEntry objRootDSE = new DirectoryEntry(path, PSUsername, PSPassword);
            var objSearcher = new System.DirectoryServices.DirectorySearcher(objRootDSE);
            objSearcher.Filter = string.Format("(&(UserPrincipalName={0}))", userPrincipalName);
            SearchResultCollection properties = objSearcher.FindAll();

            ResultPropertyValueCollection resPropertyCollection = properties[0].Properties[propertyToSearchFor];
            foreach (string resProperty in resPropertyCollection)
            {
                returnValue.Add(resProperty);
            }
        }
        catch (Exception ex)
        {
            returnValue.Add(ex.Message);
            throw;
        }

        return returnValue;
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.