ในวิธีที่ไม่คงที่ฉันสามารถใช้this.GetType()
และมันจะส่งคืนไฟล์Type
. ฉันจะทำให้เหมือนกันType
ในวิธีการคงที่ได้อย่างไร แน่นอนฉันไม่สามารถเขียนได้typeof(ThisTypeName)
เพราะThisTypeName
เป็นที่รู้จักในรันไทม์เท่านั้น ขอบคุณ!
ในวิธีที่ไม่คงที่ฉันสามารถใช้this.GetType()
และมันจะส่งคืนไฟล์Type
. ฉันจะทำให้เหมือนกันType
ในวิธีการคงที่ได้อย่างไร แน่นอนฉันไม่สามารถเขียนได้typeof(ThisTypeName)
เพราะThisTypeName
เป็นที่รู้จักในรันไทม์เท่านั้น ขอบคุณ!
คำตอบ:
หากคุณกำลังมองหา 1 ซับที่เทียบเท่ากับthis.GetType()
วิธีการคงที่ให้ลองทำดังต่อไปนี้
Type t = MethodBase.GetCurrentMethod().DeclaringType
แม้ว่าจะมีราคาแพงกว่าการใช้เพียงtypeof(TheTypeName)
อย่างเดียว
typeof(TheTypeName)
อยู่แล้ว
มีบางอย่างที่คำตอบอื่น ๆ ยังไม่ชัดเจนและเกี่ยวข้องกับแนวคิดของคุณเกี่ยวกับประเภทที่พร้อมใช้งานในเวลาดำเนินการเท่านั้น
หากคุณใช้ชนิดที่ได้รับเพื่อดำเนินการกับสมาชิกแบบคงที่ชื่อชนิดจริงจะถูกละไว้ในไบนารี ตัวอย่างเช่นรวบรวมรหัสนี้:
UnicodeEncoding.GetEncoding(0);
ตอนนี้ใช้ ildasm กับมัน ... คุณจะเห็นว่ามีการโทรออกมาดังนี้:
IL_0002: call class [mscorlib]System.Text.Encoding
[mscorlib]System.Text.Encoding::GetEncoding(int32)
คอมไพเลอร์ได้แก้ไขการเรียกไปที่Encoding.GetEncoding
- ไม่มีร่องรอยUnicodeEncoding
ด้านซ้าย นั่นทำให้ความคิดของคุณเกี่ยวกับ "ประเภทปัจจุบัน" เป็นเรื่องไร้สาระฉันกลัว
อีกวิธีหนึ่งคือการใช้ประเภท selfreferecing
//My base class
//I add a type to my base class use that in the static method to check the type of the caller.
public class Parent<TSelfReferenceType>
{
public static Type GetType()
{
return typeof(TSelfReferenceType);
}
}
จากนั้นในคลาสที่สืบทอดมาฉันสร้างประเภทการอ้างอิงตัวเอง:
public class Child: Parent<Child>
{
}
ตอนนี้ประเภทการโทร typeof (TSelfReferenceType) ภายใน Parent จะได้รับและส่งคืน Type ของผู้โทรโดยไม่จำเป็นต้องมีอินสแตนซ์
Child.GetType();
-ปล้น
คุณไม่สามารถใช้this
ในวิธีการคงที่ดังนั้นจึงไม่สามารถทำได้โดยตรง อย่างไรก็ตามหากคุณต้องการประเภทของวัตถุบางอย่างเพียงแค่เรียกGetType
ใช้และกำหนดให้this
อินสแตนซ์เป็นพารามิเตอร์ที่คุณต้องส่งผ่านเช่น:
public class Car {
public static void Drive(Car c) {
Console.WriteLine("Driving a {0}", c.GetType());
}
}
ดูเหมือนว่าจะเป็นการออกแบบที่ไม่ดี คุณแน่ใจหรือไม่ว่าคุณจำเป็นต้องได้รับประเภทของอินสแตนซ์ภายในวิธีการคงที่ของมันเองจริงๆ ดูเหมือนจะแปลกประหลาดเล็กน้อย ทำไมไม่ใช้วิธีการอินสแตนซ์ล่ะ
public class Car {
public void Drive() { // Remove parameter; doesn't need to be static.
Console.WriteLine("Driving a {0}", this.GetType());
}
}
ฉันไม่เข้าใจว่าทำไมคุณถึงใช้ typeof (ThisTypeName) ไม่ได้ หากเป็นประเภทที่ไม่ใช่ทั่วไปสิ่งนี้ควรใช้งานได้:
class Foo {
static void Method1 () {
Type t = typeof (Foo); // Can just hard code this
}
}
หากเป็นประเภททั่วไปให้ทำดังนี้
class Foo<T> {
static void Method1 () {
Type t = typeof (Foo<T>);
}
}
ฉันพลาดอะไรบางอย่างที่ชัดเจนที่นี่?
เมื่อสมาชิกของคุณอยู่ในสถานะคงที่คุณจะรู้เสมอว่าประเภทใดเป็นส่วนหนึ่งของรันไทม์ ในกรณีนี้:
class A
{
public static int GetInt(){}
}
class B : A {}
คุณไม่สามารถโทรได้ (แก้ไข: เห็นได้ชัดว่าคุณสามารถดูความคิดเห็นด้านล่าง แต่คุณยังคงโทรหา A):
B.GetInt();
เนื่องจากสมาชิกเป็นแบบคงที่จึงไม่ได้มีส่วนในสถานการณ์การสืบทอด Ergo คุณมักจะรู้ว่าประเภทคือ A
สำหรับจุดประสงค์ของฉันฉันชอบความคิดของ @ T-moty แม้ว่าฉันจะใช้ข้อมูล "ชนิดอ้างอิงตัวเอง" มาหลายปีแล้ว แต่การอ้างอิงคลาสพื้นฐานนั้นทำได้ยากกว่าในภายหลัง
ตัวอย่างเช่น (โดยใช้ @Rob Leclerc ตัวอย่างจากด้านบน):
public class ChildA: Parent<ChildA>
{
}
public class ChildB: Parent<ChildB>
{
}
การทำงานกับรูปแบบนี้อาจเป็นเรื่องท้าทายตัวอย่างเช่น คุณคืนคลาสพื้นฐานจากการเรียกใช้ฟังก์ชันได้อย่างไร
public Parent<???> GetParent() {}
หรือเมื่อพิมพ์หล่อ?
var c = (Parent<???>) GetSomeParent();
ดังนั้นฉันจึงพยายามหลีกเลี่ยงเมื่อทำได้และใช้เมื่อต้อง หากคุณต้องการฉันขอแนะนำให้คุณทำตามรูปแบบนี้:
class BaseClass
{
// All non-derived class methods goes here...
// For example:
public int Id { get; private set; }
public string Name { get; private set; }
public void Run() {}
}
class BaseClass<TSelfReferenceType> : BaseClass
{
// All derived class methods goes here...
// For example:
public TSelfReferenceType Foo() {}
public void Bar(TSelfRefenceType obj) {}
}
ตอนนี้คุณสามารถทำงานกับไฟล์BaseClass
. อย่างไรก็ตามมีหลายครั้งเช่นเดียวกับสถานการณ์ปัจจุบันของฉันที่ไม่จำเป็นต้องเปิดเผยคลาสที่ได้รับจากภายในคลาสพื้นฐานและการใช้คำแนะนำของ @M-moty ก็อาจเป็นแนวทางที่ถูกต้อง
อย่างไรก็ตามการใช้โค้ดของ @ M-moty จะใช้ได้ตราบเท่าที่คลาสพื้นฐานไม่มีตัวสร้างอินสแตนซ์ใด ๆ ใน call stack น่าเสียดายที่คลาสพื้นฐานของฉันใช้ตัวสร้างอินสแตนซ์
ดังนั้นนี่คือวิธีการขยายของฉันที่คำนึงถึงตัวสร้าง 'อินสแตนซ์' คลาสพื้นฐาน:
public static class TypeExtensions
{
public static Type GetDrivedType(this Type type, int maxSearchDepth = 10)
{
if (maxSearchDepth < 0)
throw new ArgumentOutOfRangeException(nameof(maxSearchDepth), "Must be greater than 0.");
const int skipFrames = 2; // Skip the call to self, skip the call to the static Ctor.
var stack = new StackTrace();
var maxCount = Math.Min(maxSearchDepth + skipFrames + 1, stack.FrameCount);
var frame = skipFrames;
// Skip all the base class 'instance' ctor calls.
//
while (frame < maxCount)
{
var method = stack.GetFrame(frame).GetMethod();
var declaringType = method.DeclaringType;
if (type.IsAssignableFrom(declaringType))
return declaringType;
frame++;
}
return null;
}
}
แก้ไข วิธีนี้จะใช้ได้เฉพาะเมื่อคุณปรับใช้ไฟล์ PDB กับไฟล์ปฏิบัติการ / ไลบรารีดังที่markmnlชี้ให้ฉันเห็น
มิฉะนั้นจะเป็นปัญหาใหญ่ที่ต้องตรวจพบ: ทำงานได้ดีในการพัฒนา แต่อาจไม่ได้อยู่ในการผลิต
วิธียูทิลิตี้เพียงแค่เรียกวิธีการเมื่อคุณต้องการจากทุกที่ของรหัสของคุณ:
public static Type GetType()
{
var stack = new System.Diagnostics.StackTrace();
if (stack.FrameCount < 2)
return null;
return (stack.GetFrame(1).GetMethod() as System.Reflection.MethodInfo).DeclaringType;
}