Gọi một phương thức tĩnh trên một tham số kiểu chung


107

Tôi đã hy vọng làm điều gì đó như thế này, nhưng nó có vẻ là bất hợp pháp trong C #:


public Collection MethodThatFetchesSomething<T>()
    where T : SomeBaseClass
{
    return T.StaticMethodOnSomeBaseClassThatReturnsCollection();
}

Tôi gặp lỗi thời gian biên dịch: "'T' là 'tham số kiểu', không hợp lệ trong ngữ cảnh nhất định."

Với một tham số kiểu chung, làm cách nào để gọi một phương thức tĩnh trên lớp chung? Phương thức tĩnh phải có sẵn, với ràng buộc.


Câu trả lời:


58

Trong trường hợp này, bạn chỉ nên gọi trực tiếp phương thức tĩnh trên kiểu bị ràng buộc. C # (và CLR) không hỗ trợ các phương thức tĩnh ảo. Vì thế:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

... có thể không khác là:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

Đi qua tham số kiểu chung là một hướng không cần thiết và do đó không được hỗ trợ.


25
Nhưng điều gì sẽ xảy ra nếu bạn che phương thức tĩnh của mình trong một lớp con? public class SomeChildClass: SomeBaseClass {public new static StaticMethodOnSomeBaseClassThatReturnsCollection () {}} Bạn có thể làm gì đó để truy cập phương thức static đó từ một kiểu chung không?
Hugo Migneron,

2
Hãy xem câu trả lời của Joshua Pech dưới đây, tôi tin rằng điều đó sẽ hiệu quả trong trường hợp đó.
Remi Despres-Smyth

1
Sẽ return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();làm việc? Nếu vậy, bạn có thể muốn thêm điều đó vào câu trả lời của mình. Cảm ơn. Nó đã làm việc cho tôi. Trong trường hợp của tôi, lớp của tôi có một tham số kiểu nên tôi đã làm return SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();và điều đó đã hoạt động.
toddmo

27

Để giải thích rõ hơn về câu trả lời trước, tôi nghĩ rằng sự phản ánh gần với những gì bạn muốn ở đây. Tôi có thể đưa ra 1001 lý do tại sao bạn nên hoặc không nên làm điều gì đó, tôi sẽ chỉ trả lời câu hỏi của bạn như đã hỏi. Tôi nghĩ bạn nên gọi phương thức GetMethod trên loại tham số chung và đi từ đó. Ví dụ, đối với một hàm:

public void doSomething<T>() where T : someParent
{
    List<T> items=(List<T>)typeof(T).GetMethod("fetchAll").Invoke(null,new object[]{});
    //do something with items
}

Trong đó T là bất kỳ lớp nào có phương thức tĩnh fetchAll ().

Có, tôi biết rằng điều này rất chậm và có thể gặp sự cố nếu someParent không buộc tất cả các lớp con của nó thực hiện fetchAll nhưng nó trả lời câu hỏi như đã hỏi.


2
Không hoàn toàn không. JaredPar đã hoàn toàn đúng: T.StaticMethodOnSomeBaseClassThatReturnsCollection trong đó T: SomeBaseClass không khác gì chỉ đơn giản là chỉ ra SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection.
Remi Despres-Smyth

2
Đây là những gì tôi cần, nó hoạt động với một phương pháp tĩnh
myro

Đây là câu trả lời tôi cần vì tôi không có quyền kiểm soát các lớp và lớp cơ sở.
Tim

8

Cách duy nhất để gọi một phương thức như vậy là thông qua phản xạ, Tuy nhiên, có vẻ như có thể gói chức năng đó trong một giao diện và sử dụng mẫu IoC / factory / etc dựa trên phiên bản.


5

Có vẻ như bạn đang cố gắng sử dụng generic để giải quyết thực tế là không có "phương thức tĩnh ảo" nào trong C #.

Thật không may, điều đó sẽ không hoạt động.


1
Tôi không - tôi đang làm việc trên một lớp DAL đã tạo. Các lớp được tạo đều kế thừa từ một lớp cơ sở, có phương thức FetchAll tĩnh. Tôi đang cố gắng giảm sự trùng lặp mã trong lớp kho lưu trữ của mình bằng lớp kho lưu trữ "chung" - rất nhiều mã lặp lại, ngoại trừ lớp cụ thể được sử dụng.
Remi Despres-Smyth

1
Vậy tại sao bạn không gọi SomeBaseClass.StaticMethod ... ()?
Brad Wilson

Xin lỗi, tôi đã không giải thích rõ về bản thân trong bình luận trước. FetchAll được định nghĩa trên cơ sở, nhưng được triển khai trên các lớp dẫn xuất. Tôi cần gọi nó trên lớp dẫn xuất.
Remi Despres-Smyth

7
Nếu nó là một phương thức tĩnh, thì nó vừa được định nghĩa và thực hiện bởi cơ sở. Không có cái gọi là phương thức tĩnh ảo / trừu tượng trong C # và không có ghi đè nào cho điều đó. Tôi nghi ngờ bạn đã khai báo lại đơn giản, điều này rất khác.
Marc Gravell

1
Vâng, bạn nói đúng - tôi đã đưa ra những giả định không hợp lệ ở đây. Cảm ơn vì cuộc thảo luận đã giúp đưa tôi đi đúng hướng.
Remi Despres-Smyth

2

Như bây giờ, bạn không thể. Bạn cần một cách để nói với trình biên dịch rằng T có phương pháp đó, và hiện tại, không có cách nào để làm điều đó. (Nhiều người đang thúc đẩy Microsoft mở rộng những gì có thể được chỉ định trong một ràng buộc chung, vì vậy có thể điều này sẽ khả thi trong tương lai).


1
Vấn đề là vì các generic được cung cấp bởi thời gian chạy, điều này có thể có nghĩa là một phiên bản CLR mới - mà họ (phần lớn) đã tránh kể từ phiên bản 2.0. Tuy nhiên, có lẽ chúng tôi sắp sửa một cái mới ...
Marc Gravell

2

Ở đây, tôi đăng một ví dụ hoạt động, đó là một giải pháp

public interface eInterface {
    void MethodOnSomeBaseClassThatReturnsCollection();
}

public T:SomeBaseClass, eInterface {

   public void MethodOnSomeBaseClassThatReturnsCollection() 
   { StaticMethodOnSomeBaseClassThatReturnsCollection() }

}

public Collection MethodThatFetchesSomething<T>() where T : SomeBaseClass, eInterface
{ 
   return ((eInterface)(new T()).StaticMethodOnSomeBaseClassThatReturnsCollection();
}

2
Điều này mang lại một lỗi cú pháp cho tôi? Nghĩa public T : SomeBaseClasslà gì?
Eric

Nếu lớp của bạn có phương thức instance someInstanceMethod (), bạn luôn có thể gọi nó bằng cách thực hiện (new T ()). SomeInstanceMethod (); - nhưng đây là cách gọi một phương thức thể hiện - câu hỏi đặt ra cách gọi một phương thức tĩnh của lớp.
timothy

2

Tôi chỉ muốn ném ra rằng đôi khi các đại biểu giải quyết những vấn đề này, tùy thuộc vào ngữ cảnh.

Nếu bạn cần gọi phương thức tĩnh dưới dạng một nhà máy hoặc phương thức khởi tạo nào đó, thì bạn có thể khai báo một ủy nhiệm và chuyển phương thức tĩnh cho nhà máy chung có liên quan hoặc bất kỳ phương thức nào cần "lớp chung với phương thức tĩnh này".

Ví dụ:

class Factory<TProduct> where TProduct : new()
{
    public delegate void ProductInitializationMethod(TProduct newProduct);


    private ProductInitializationMethod m_ProductInitializationMethod;


    public Factory(ProductInitializationMethod p_ProductInitializationMethod)
    {
        m_ProductInitializationMethod = p_ProductInitializationMethod;
    }

    public TProduct CreateProduct()
    {
        var prod = new TProduct();
        m_ProductInitializationMethod(prod);
        return prod;
    }
}

class ProductA
{
    public static void InitializeProduct(ProductA newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class ProductB
{
    public static void InitializeProduct(ProductB newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class GenericAndDelegateTest
{
    public static void Main()
    {
        var factoryA = new Factory<ProductA>(ProductA.InitializeProduct);
        var factoryB = new Factory<ProductB>(ProductB.InitializeProduct);

        ProductA prodA = factoryA.CreateProduct();
        ProductB prodB = factoryB.CreateProduct();
    }
}

Thật không may, bạn không thể thực thi rằng lớp có phương thức phù hợp, nhưng ít nhất bạn có thể biên dịch-thời gian-thực thi rằng phương thức gốc kết quả có mọi thứ mà nó mong đợi (tức là một phương thức khởi tạo với chính xác chữ ký phù hợp). Điều này tốt hơn một ngoại lệ phản ánh thời gian chạy.

Cách tiếp cận này cũng có một số lợi ích, tức là bạn có thể sử dụng lại các phương thức init, đặt chúng là phương thức thể hiện, v.v.


1

Bạn có thể làm điều này bằng cách sử dụng phản chiếu, như được mô tả ở đây

Do liên kết đã chết, tôi tìm thấy các chi tiết liên quan trong máy quay lại:

Giả sử bạn có một lớp với một phương thức chung tĩnh:

class ClassWithGenericStaticMethod
{
    public static void PrintName<T>(string prefix) where T : class
    {
        Console.WriteLine(prefix + " " + typeof(T).FullName);
    }
}

Làm thế nào bạn có thể gọi phương pháp này bằng cách sử dụng chuyển hạng?

Hóa ra là rất dễ dàng… Đây là cách bạn gọi một phương pháp chung tĩnh bằng cách sử dụng Reflection:

// Grabbing the type that has the static generic method
Type typeofClassWithGenericStaticMethod = typeof(ClassWithGenericStaticMethod);

// Grabbing the specific static method
MethodInfo methodInfo = typeofClassWithGenericStaticMethod.GetMethod("PrintName", System.Reflection.BindingFlags.Static | BindingFlags.Public);

// Binding the method info to generic arguments
Type[] genericArguments = new Type[] { typeof(Program) };
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);

// Simply invoking the method and passing parameters
// The null parameter is the object to call the method from. Since the method is
// static, pass null.
object returnValue = genericMethodInfo.Invoke(null, new object[] { "hello" });

Liên kết đã chết.
Necronomicron
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.