.NET: Xác định kiểu của lớp “this” trong phương thức tĩnh của nó


94

Trong một phương thức không tĩnh, tôi có thể sử dụng this.GetType()và nó sẽ trả về Type. Làm thế nào tôi có thể nhận được điều tương tự Typetrong một phương thức tĩnh? Tất nhiên, tôi không thể chỉ viết typeof(ThisTypeName)ThisTypeNamechỉ được biết trong thời gian chạy. Cảm ơn!


16
Bạn đang ở trong một ngữ cảnh STATIC và không thể viết typeof (ThisTypeName)? Làm sao?
Bruno Reis

1
Không có gì giống như 'thời gian chạy' bên trong một phương thức tĩnh (giả sử bạn không nói về một đối số được truyền cho một phương thức tĩnh). Trong trường hợp đó, bạn có thể chỉ cần nói typeof (RelevantType).
Manish Basantani

2
Một phương thức tĩnh không thể là ảo. Bạn đã biết loại.
Hans Passant

6
Sẽ có nhiều lớp dẫn xuất từ ​​một lớp trừu tượng. Lớp trừu tượng cơ sở có từ điển tĩnh <Int, Type>. Vì vậy, các lớp dẫn xuất tự “đăng ký” trong các hàm tạo tĩnh (dic.Add (N, T)). Và vâng, tôi biết loại :) Tôi chỉ hơi lười biếng và không thích thay thế văn bản và tự hỏi liệu "T" có thể được xác định trong thời gian chạy hay không. Xin thứ lỗi cho lời nói dối của tôi, vì cần phải đơn giản hóa câu hỏi. Và nó đã hoạt động;) Hiện có một giải pháp được chấp nhận. Cảm ơn.
Yegor

Một lớp con kế thừa các phương thức tĩnh của lớp cha của nó, không? Sẽ không hợp lý nếu một phương thức tĩnh của lớp cha hữu ích cho tất cả các lớp con của nó? Static đơn giản có nghĩa là không có instance, chắc chắn nguyên tắc của mã phổ biến trong một lớp cơ sở chung áp dụng cho các phương thức static cũng như các phương thức instance?
Matt Connolly

Câu trả lời:


134

Nếu bạn đang tìm kiếm 1 lớp lót tương đương this.GetType()với các phương thức tĩnh, hãy thử cách sau.

Type t = MethodBase.GetCurrentMethod().DeclaringType

Mặc dù điều này có thể đắt hơn nhiều so với việc chỉ sử dụng typeof(TheTypeName).


1
Cái này hoạt động tốt. Cảm ơn :) Nó không đắt như vậy vì nó sẽ được gọi là khá hiếm.
Yegor

2
Entrase, bởi Jared "đắt tiền" có nghĩa là chúng tốn kém cho bộ xử lý, thường có nghĩa là chậm. Nhưng anh ấy nói, "đắt hơn nhiều" nghĩa là chậm hơn. Có lẽ không chậm chút nào, trừ khi bạn đang thiết kế một hệ thống dẫn đường cho tên lửa.
Dan Rosenstark

1
Tôi đã thấy GetCurrentMethod gây ra một số vấn đề nghiêm trọng về hiệu suất. Nhưng vì bạn chỉ nhận được loại bạn có thể lưu vào bộ nhớ cache.
Jonathan Allen

51
Điều này luôn trả về lớp thực thi phương thức hiện tại, không phải lớp mà nó được gọi trong trường hợp các lớp con.
Matt Connolly

3
Tôi đoán Thật tiện lợi để tránh lỗi nếu mã được di chuyển sang các tên lớp khác nhau hoặc thứ gì đó, nhưng typeof(TheTypeName)dù sao thì một công cụ tái cấu trúc tốt cũng nên giải quyết vấn đề này.
Nyerguds

59

Có điều gì đó mà các câu trả lời khác chưa hoàn toàn làm rõ và có liên quan đến ý tưởng của bạn về loại chỉ khả dụng tại thời điểm thực thi.

Nếu bạn sử dụng kiểu dẫn xuất để thực thi một thành viên tĩnh, tên kiểu thực sẽ bị bỏ qua trong tệp nhị phân. Vì vậy, ví dụ, biên dịch mã này:

UnicodeEncoding.GetEncoding(0);

Bây giờ sử dụng ildasm trên nó ... bạn sẽ thấy rằng cuộc gọi được phát ra như thế này:

IL_0002:  call       class [mscorlib]System.Text.Encoding 
[mscorlib]System.Text.Encoding::GetEncoding(int32)

Trình biên dịch đã giải quyết lệnh gọi đến Encoding.GetEncoding- không còn dấu vết nào UnicodeEncoding. Điều đó làm cho ý tưởng của bạn về "kiểu hiện tại" trở nên vô nghĩa, tôi e rằng.


24

Một giải pháp khác là sử dụng kiểu tự tham khảo

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

Sau đó, trong lớp kế thừa nó, tôi tạo một kiểu tự tham chiếu:

public class Child: Parent<Child>
{
}

Bây giờ kiểu gọi typeof (TSelfReferenceType) bên trong Parent sẽ lấy và trả về Kiểu của người gọi mà không cần thể hiện.

Child.GetType();

-Rob


Tôi đã sử dụng điều này cho các mẫu singleton, tức là Singleton <T> ... các thành viên tĩnh sau đó có thể tham chiếu đến typeof (T) trong thông báo lỗi hoặc bất cứ nơi nào khác cần thiết.
yoyo

1
Chào. Tôi thực sự thích và đánh giá cao câu trả lời này vì nó giúp tôi tìm ra kiểu con từ một hàm cơ sở tĩnh.
Kỹ sư phần mềm Bill

1
Tốt lắm. Tuy nhiên, quá buồn rằng trong C # điều này không thể được thực hiện nếu không có sự trùng lặp mã nhỏ này.
Tên hiển thị

Có một ví dụ khác về cách giải quyết này trên stackoverflow.com/a/22532416/448568
Steven de Salas

6

Bạn không thể sử dụng thistrong một phương thức tĩnh, vì vậy điều đó không thể trực tiếp được. Tuy nhiên, nếu bạn cần kiểu của đối tượng nào đó, chỉ cần gọi GetTypenó và đặt đối tượng đó thành thismột tham số mà bạn phải truyền, ví dụ:

public class Car {
  public static void Drive(Car c) {
    Console.WriteLine("Driving a {0}", c.GetType());
  }
}

Tuy nhiên, đây có vẻ là một thiết kế kém. Bạn có chắc chắn rằng bạn thực sự cần lấy kiểu của cá thể bên trong phương thức tĩnh của chính nó không? Điều đó có vẻ hơi kỳ lạ. Tại sao không chỉ sử dụng một phương thức thể hiện?

public class Car {
  public void Drive() { // Remove parameter; doesn't need to be static.
    Console.WriteLine("Driving a {0}", this.GetType());
  }
}

3

Tôi không hiểu tại sao bạn không thể sử dụng typeof (ThisTypeName). Nếu đây là một loại không chung chung, thì nó sẽ hoạt động:

class Foo {
   static void Method1 () {
      Type t = typeof (Foo); // Can just hard code this
   }
}

Nếu đó là một loại chung, thì:

class Foo<T> {
    static void Method1 () {
       Type t = typeof (Foo<T>);
    }
}

Tôi có thiếu một cái gì đó rõ ràng ở đây?


7
Điều này sẽ không hoạt động nếu bạn tạo một lớp Bar có nguồn gốc từ Foo và sau đó lớp kế thừa Method1 - sau đó một lệnh gọi đến Bar.Method1 sẽ vẫn xử lý typeof (Foo) bị sai. Method1 được kế thừa bằng cách nào đó sẽ biết rằng nó được đặt trong Bar, và sau đó nhận được typeof (Bar).
JustAMartin

0

Khi thành viên của bạn ở trạng thái tĩnh, bạn sẽ luôn biết nó thuộc loại nào trong thời gian chạy. Trong trường hợp này:

class A
{
  public static int GetInt(){}

}
class B : A {}

Bạn không thể gọi (chỉnh sửa: rõ ràng, bạn có thể, xem bình luận bên dưới, nhưng bạn vẫn sẽ gọi vào A):

B.GetInt();

bởi vì thành viên là tĩnh, nó không đóng vai trò trong các kịch bản kế thừa. Ergo, bạn luôn biết rằng loại là A.


4
Bạn có thể gọi B.GetInt () - ít nhất là bạn có thể nếu nó không phải là private - nhưng trình biên dịch sẽ dịch nó thành một cuộc gọi tới A.GetInt (). Thử nó!
Jon Skeet

Lưu ý về nhận xét của Jon: khả năng hiển thị mặc định trong C # là riêng tư, do đó ví dụ của bạn không hoạt động.
Dan Rosenstark

0

Đối với mục đích của tôi, tôi thích ý tưởng của @ T-moty. Mặc dù tôi đã sử dụng thông tin "kiểu tự tham chiếu" trong nhiều năm, việc tham chiếu lớp cơ sở sau này khó thực hiện hơn.

Ví dụ (sử dụng ví dụ @Rob Leclerc ở trên):

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

Ví dụ, làm việc với mẫu này có thể khó khăn; làm thế nào để bạn trả về lớp cơ sở từ một lời gọi hàm?

public Parent<???> GetParent() {}

Hoặc khi loại đúc?

var c = (Parent<???>) GetSomeParent();

Vì vậy, tôi cố gắng tránh nó khi tôi có thể, và sử dụng nó khi tôi phải. Nếu bạn bắt buộc phải làm, tôi khuyên bạn nên làm theo mẫu sau:

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) {}
}

Bây giờ bạn có thể (nhiều hơn) dễ dàng làm việc với BaseClass. Tuy nhiên, có những lúc, giống như tình huống hiện tại của tôi, việc hiển thị lớp dẫn xuất, từ bên trong lớp cơ sở, là không cần thiết và sử dụng gợi ý của @ M-moty có thể là cách tiếp cận phù hợp.

Tuy nhiên, việc sử dụng mã của @ M-moty chỉ hoạt động miễn là lớp cơ sở không chứa bất kỳ hàm tạo cá thể nào trong ngăn xếp cuộc gọi. Thật không may, các lớp cơ sở của tôi sử dụng các hàm tạo cá thể.

Do đó, đây là phương thức mở rộng của tôi có tính đến các hàm tạo 'phiên bản' của lớp cơ sở:

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

0

CHỈNH SỬA Phương pháp này sẽ chỉ hoạt động khi bạn triển khai các tệp PDB với tệp thực thi / thư viện, như markmnl đã chỉ ra cho tôi.

Nếu không sẽ là một vấn đề lớn cần được phát hiện: hoạt động tốt trong quá trình phát triển, nhưng có thể không hoạt động trong quá trình sản xuất.


Phương thức tiện ích, chỉ cần gọi phương thức khi bạn cần, từ mọi vị trí mã của bạn:

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

1
StackTrace chỉ có sẵn trong Debug xây dựng
markmnl

Không đúng: StackTrace sẽ khả dụng khi bạn cũng triển khai tệp .pdb ở chế độ phát hành. stackoverflow.com/questions/2345957/…
T-moty

Tôi có điểm số của bạn. Không thể chấp nhận rằng một phương pháp chỉ hoạt động khi các tệp PDB được triển khai. Tôi sẽ chỉnh sửa câu trả lời
T-moty
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.