การซ่อนและการสะท้อนคุณสมบัติ (C#)

การประกาศคุณสมบัติในคลาสที่ได้รับซึ่งตรงกับชื่อของคุณสมบัติในคลาสฐานจะ "ซ่อน" คุณสมบัตินั้น (เว้นแต่จะแทนที่คุณสมบัติด้วยคีย์เวิร์ด override) ทั้งคุณสมบัติฐานและคลาสที่ได้รับจะถูกส่งกลับโดย Type.GetProperties() หากประเภทไม่ตรงกัน อย่างไรก็ตาม หากประเภท do ตรงกัน จะส่งคืนเฉพาะคุณสมบัติของคลาสที่ได้รับอย่างน่าตกใจเท่านั้น ตัวอย่างเช่น:

class A
{
    protected double p;
    public int P { get { return (int)p; } set { p = value; } }
}
class B : A
{
    public new int P { get { return (int)p; } set { p = value; } }
}
class C : B
{
    public new float P { get { return (float)p; } set { p = value; } }
}

การโทร typeof(C).GetProperties() จะส่งกลับเฉพาะ B.P และ C.P. เป็นไปได้ไหมที่จะโทร GetProperties() ในลักษณะที่ส่งคืนทั้งสาม? เกือบจะแน่นอนมีวิธีที่จะทำได้โดยการสำรวจลำดับชั้นการสืบทอด แต่มีวิธีแก้ปัญหาที่สะอาดกว่าหรือไม่


person Eric Mickelsen    schedule 26.04.2010    source แหล่งที่มา
comment
ทำไมคุณถึงอยากมีลำดับชั้นเช่นนี้?   -  person driis    schedule 26.04.2010
comment
@driis: นี่เป็นเพียงตัวอย่างเล็กน้อยที่จะแสดง แต่เพื่อการโต้แย้ง สมมติว่าลำดับชั้นนี้เขียนโดยบุคคลอื่นที่ไม่ใช่ฉัน แต่ฉันยังคงต้องหาทรัพย์สินสาธารณะทั้งหมด แม้แต่ทรัพย์สินที่ซ่อนอยู่   -  person Eric Mickelsen    schedule 26.04.2010
comment
เนื่องจากไม่มีทางที่การใช้คลาสที่ได้รับจะสามารถเข้าถึงคุณสมบัติที่ซ่อนอยู่ได้ ฉันจึงคิดว่ามันไม่มีคุณสมบัตินั้นสำหรับการสะท้อนกลับอย่างมีประสิทธิภาพ คิดว่าตัวเองโชคดี VB จะแสดงเฉพาะ CP เมื่อใช้ Shadows   -  person Wilhelm    schedule 26.04.2010
comment
@Wilhelm: แต่คุณสามารถเข้าถึงคุณสมบัติที่ซ่อนอยู่: C c = new C(); ((A)ค).P = 42;   -  person Eric Mickelsen    schedule 26.04.2010
comment
คุณได้ลองใช้ BindingFlags.FlattenHierarchy แล้วหรือยัง? ที่กล่าวว่าการไม่คืนทรัพย์สินฟังดูเหมือนเป็นพฤติกรรมที่ถูกต้อง เนื่องจากอาจถูกซ่อนไว้ด้วยเหตุผลบางอย่าง   -  person Dan Bryant    schedule 26.04.2010
comment
@Dan: FlattenHierarchy มีไว้สำหรับสมาชิกแบบคงที่ มันจะไม่ทำงานที่นี่   -  person Eric Mickelsen    schedule 26.04.2010
comment
ฉันชอบทั้งสองคำตอบจนถึงตอนนี้ค่อนข้างดี ฉันตัดสินใจที่จะโพสต์เงินรางวัลและให้เวลาเพิ่มอีกเพื่อดูว่ามีใครสามารถปรับปรุงสิ่งเหล่านั้นได้หรือไม่ เนื่องจากแต่ละคนมีข้อเสียเปรียบที่สำคัญ เป็นที่น่าสนใจว่าทั้งสาขาหรือวิธีการไม่มีปัญหาเรื่องการสะท้อนกลับ คำอธิบายที่น่าพอใจสำหรับปัญหานี้อาจเป็นคำตอบที่ยอมรับได้   -  person Eric Mickelsen    schedule 29.04.2010


คำตอบ (3)


GetProperties ถูกกำหนดให้เป็นคุณสมบัติสาธารณะทั้งหมดของประเภท

คุณสามารถรับและตั้งค่าวิธีการโดยใช้:

typeof(C).GetMethods()
         .Where(m => m.Name.StartsWith("set_") || m.Name.StartsWith("get_"))

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

person Yuriy Faktorovich    schedule 26.04.2010
comment
ฉันคิดว่าคุณคงประสบปัญหาเดียวกันกับเมธอด get_P ที่ซ่อนหนึ่งในพาเรนต์ - person driis; 26.04.2010
comment
@Yuriy: typeof(C).GetMethods().Where @driis: ไม่ ดูเหมือนว่าจะทำงานได้อย่างถูกต้อง - person Eric Mickelsen; 26.04.2010
comment
คุณพูดถูก วิธีนี้ก็ใช้ได้ผลเช่นกัน ไม่ค่อยแน่ใจว่าทำไมมันถึงใช้งานได้ เนื่องจากฉันถือว่ากฎการซ่อนวิธีการเดียวกันนั้นใช้สำหรับวิธีการและคุณสมบัติ อืม. - person driis; 26.04.2010
comment
สิ่งนี้จะได้ผลสำหรับฉันถ้าฉันสามารถรับ PropertyInfo จาก getter หรือ setter ได้ - person Eric Mickelsen; 26.04.2010
comment
คุณต้องการอะไรจาก PropertyInfo ที่ไม่ได้จัดเตรียมโดย getter และ setter? - person Yuriy Faktorovich; 26.04.2010
comment
ฉันคิดว่ามันจะทำงานได้หากไม่มี PropertyInfo แต่ฉันจะต้องสามารถจับคู่ getter แต่ละตัวกับ setter ที่เกี่ยวข้องได้อย่างมีประสิทธิภาพ และนั่นดูยุ่งเหยิง - person Eric Mickelsen; 26.04.2010
comment
แม้จะไม่ค่อยสะอาดนัก แต่จนถึงขณะนี้นี่เป็นวิธี เท่านั้น ที่ให้ผลตอบแทนเป็นจำนวนคุณสมบัติที่ถูกต้องจริงๆ ดังนั้นจึงเป็นคำตอบที่ฉันยอมรับ มันยังคงน่าทึ่งสำหรับฉันที่การสะท้อนกลับสามารถทำงานได้อย่างดีในสนามและวิธีการต่างๆ แต่กลับแบนราบลงบนใบหน้าด้วยคุณสมบัติ - person Eric Mickelsen; 05.05.2010

ฉันไม่คิดว่ามันเป็นไปได้หากไม่ข้ามลำดับชั้นการสืบทอด ไม่จำเป็นต้องเป็นโค้ดมากเกินไป:

    public static IEnumerable<PropertyInfo> GetAllProperties(Type t)
    {
        while (t != typeof(object))
        {
            foreach (var prop in t.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
                yield return prop;
            t = t.BaseType;
        }
    }

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

person driis    schedule 26.04.2010
comment
สิ่งนี้จะไม่ให้คุณสมบัติที่ถูกแทนที่หลายครั้งใช่ไหม - person Eric Mickelsen; 26.04.2010
comment
ไม่ เพราะเขาบอกว่า BindingFlags.DeclaredOnly - person Yuriy Faktorovich; 26.04.2010
comment
ปัญหาคือ ถ้าเราเขียนลำดับชั้นของคลาสใหม่เพื่อให้ B.P แทนที่ A.P เรายังคงได้รับคุณสมบัติสามประการ แม้ว่าตอนนี้วัตถุประเภท C จะมีเพียงสองเท่านั้น - person Eric Mickelsen; 26.04.2010
comment
ในทางตรงกันข้าม Yuriy คำตอบของคุณจะให้ค่า get/setters เพียงสองชุดเท่านั้น ซึ่งเป็นพฤติกรรมที่ถูกต้อง - person Eric Mickelsen; 26.04.2010
comment
คุณพูดถูก ฉันอ่านสิ่งที่คุณพูดผิด ฉันทดสอบวิธีการของฉันด้วยและมันส่งคืนเพียงสองวิธีเท่านั้น - person Yuriy Faktorovich; 26.04.2010

ด้วยการไตร่ตรอง คีย์เวิร์ดใหม่จะซ่อนเฉพาะคุณสมบัติที่สืบทอดมาหากลายเซ็นตรงกัน ฉันเดาว่าการสะท้อนกลับตรงกับลายเซ็นของผู้เข้าถึงคุณสมบัติ (get_ & set_) นี่คือสาเหตุที่ GetProperties() ส่งคืน B.P และ C.P เมื่อประเภทการส่งคืนแตกต่างกัน

ฉันเพิ่งค้นพบ Fasteflect ซึ่งมีกลไกการสะท้อนขั้นสูง

ฉันตรวจสอบแล้ว Fasteflect type.Properties ส่งคืนแผนผังทั้งหมดของสมาชิกที่ซ่อนอยู่ (P) ฉันคิดว่า API พิจารณาการสำรองสมาชิก (เสมือน / แทนที่) และสมาชิกที่ซ่อนอยู่ (ใหม่) แตกต่างกันซึ่งเป็นสิ่งที่ดีสำหรับ 'ปัญหา' ของคุณ;)

การทดสอบของฉันด้วย fastflect :

class Class1
{
    public object field1 = null;

    public virtual object Property1 { get; set; }

    public object Property2 { get; set; }

    public string Property3 { get; set; }
}

class Class2 : Class1
{
    public new object field1 = null;

    public override object Property1 { get; set; }

    public new string Property3 { get; set; }
}

class Class3 : Class2
{
    public new string Property3 { get; set; }
}

กรองสมาชิกสำรอง แต่ส่งคืนสมาชิกที่ซ่อนอยู่ทั้งหมด:

typeof(Class3).Properties(Flags.ExcludeBackingMembers | Flags.Public | Flags.Instance) 
  • typeof(Class3).Properties(Flags.ExcludeBackingMembers | Flags.Public | Flags.Instance) Count = 5 System.Collections.Generic.IList
    • [0] {System.String Property3} System.Reflection.PropertyInfo
    • [1] {System.Object Property1} System.Reflection.PropertyInfo
    • [2] {System.String Property3} System.Reflection.PropertyInfo
    • [3] {System.Object Property2} System.Reflection.PropertyInfo
    • [4] {System.String Property3} System.Reflection.PropertyInfo
person JoeBilly    schedule 04.05.2010
comment
msdn.microsoft.com/en-us/ ไลบรารี/ : ค่าคงที่ ฟิลด์ คุณสมบัติ หรือประเภทที่แนะนำในคลาสหรือโครงสร้างจะซ่อนสมาชิกคลาสพื้นฐานทั้งหมดที่มีชื่อเดียวกัน - person Eric Mickelsen; 04.05.2010
comment
ไม่ใช่ผ่านการสะท้อน เนื่องจากคุณสมบัติคือตัวเข้าถึง ฉันเดาว่าการสะท้อนกลับจะตรงกับลายเซ็นของวิธีการเข้าถึง (get_ & set_) - person JoeBilly; 04.05.2010
comment
หลังจากการทดสอบและตรวจสอบ Fasterflect แล้ว ฉันสามารถพูดได้อย่างปลอดภัยว่าคำตอบของพวกเขาสำหรับปัญหานี้เทียบเท่ากับของ driis โดยสิ้นเชิง โซลูชันแบบเรียกซ้ำเพื่อค้นหาสมาชิกที่ซ่อนอยู่ไม่สามารถยกเว้นคุณสมบัติเสมือนที่ถูกแทนที่ได้ (อันที่จริง การตั้งค่าสถานะ ExcludeBackingMembers จะยกเว้นคุณสมบัติทั้งสามในทั้งสองกรณี) อย่างไรก็ตาม ขอบคุณสำหรับลิงก์ - person Eric Mickelsen; 04.05.2010
comment
แปลกฉันเพิ่มการทดสอบของฉันในโพสต์ ฉันทดสอบอีกครั้งและ ExcludeBackingMembers ไม่ได้แยกสมาชิกที่ซ่อนอยู่ ฉันพยายามโดยไม่มีคุณสมบัติอัตโนมัติด้วย: พฤติกรรมเดียวกัน - person JoeBilly; 05.05.2010
comment
น่าสนใจ - มันทำงานได้ดีขึ้นมากกับแฟล็กที่คุณใช้ อย่างไรก็ตาม มันยังคงล้มเหลวในการทดสอบของฉัน หากคุณซ่อนคุณสมบัติที่ถูกแทนที่ คุณสมบัตินั้นจะยังคงหายไป (เพิ่มออบเจ็กต์ใหม่สาธารณะ Property1 ให้กับ Class3 ของคุณ) ดังนั้น จึงยังมีคุณสมบัติอยู่ที่นั่นที่สามารถใช้ได้ แต่จะสะท้อนกลับไม่สามารถมองเห็นได้ - person Eric Mickelsen; 05.05.2010
comment
ถูกต้องน่าสนใจจริงๆ ขอบคุณสำหรับคำพูดนี้ เป็นเพราะ Property1 ใน Class1 และ Class2 ยังคงสนับสนุนสมาชิกอยู่ ExcludeBackingMembers ทำหน้าที่ของเขาแต่เกือบจะแย่เพราะควรส่งคืน Property1 ของ Class2 ด้วย (ซึ่งกลายเป็นสมาชิกที่ซ่อนอยู่) ตามตัวดัดแปลงใหม่ใน Class3 บางทีการสืบค้นขั้นสูงอาจจัดการสิ่งนี้ได้: fasterflect.codeplex.com/ - person JoeBilly; 05.05.2010
comment
หืม ในกรณีที่: public int Prop { get; } และ public float Prop { get; } การสะท้อนกลับจะส่งคืนคุณสมบัติสองรายการ แต่เมื่อคุณมีเหตุการณ์ที่มีประเภทต่างกัน การสะท้อนกลับจะส่งกลับเพียงเหตุการณ์เดียวเท่านั้น ทั้งหมดนี้ค่อนข้างดูเหมือนเป็นแมลง - person Denis535; 14.10.2018