简体   繁体   English

带有泛型的 C# 接口静态方法调用

[英]C# interface static method call with generics

Is there a simple way to implement this, and if possible without instanciating an object :有没有一种简单的方法来实现这一点,如果可能的话,不实例化一个对象:

interface I
{
     static  string GetClassName();
}

public class Helper
{

    static void PrintClassName<T>() where T : I
    {
         Console.WriteLine(T.GetClassName());
    }
}

Try an extension method instead:尝试使用扩展方法:

public interface IMyInterface
{
     string GetClassName();
}

public static class IMyInterfaceExtensions
{
    public static void PrintClassName<T>( this T input ) 
        where T : IMyInterface
    {
         Console.WriteLine(input.GetClassName());
    }
}

This allows you to add static extension/utility method, but you still need an instance of your IMyInterface implementation.这允许您添加静态扩展/实用程序方法,但您仍然需要 IMyInterface 实现的实例。

You can't have interfaces for static methods because it wouldn't make sense, they're utility methods without an instance and hence they don't really have a type.你不能有静态方法的接口,因为它没有意义,它们是没有实例的实用方法,因此它们没有真正的类型。

You can not inherit static methods.您不能继承静态方法。 Your code wouldn't compile in any way, because a interface can't have static methods because of this.您的代码不会以任何方式编译,因为因此接口不能具有静态方法。

As quoted from littleguru :引用自littleguru

Inheritance in .NET works only on instance base. .NET 中的继承仅适用于实例基础。 Static methods are defined on the type level not on the instance level.静态方法是在类型级别而不是在实例级别定义的。 That is why overriding doesn't work with static methods/properties/events...这就是为什么覆盖不适用于静态方法/属性/事件......

Static methods are only held once in memory.静态方法只在内存中保存一次。 There is no virtual table etc. that is created for them.没有为他们创建的虚拟表等。

If you invoke an instance method in .NET, you always give it the current instance.如果在 .NET 中调用实例方法,则始终为其提供当前实例。 This is hidden by the .NET runtime, but it happens.这被 .NET 运行时隐藏,但它发生了。 Each instance method has as first argument a pointer (reference) to the object that the method is run on.每个实例方法都有一个指向运行该方法的对象的指针(引用)作为第一个参数。 This doesn't happen with static methods (as they are defined on type level).静态方法不会发生这种情况(因为它们是在类型级别定义的)。 How should the compiler decide to select the method to invoke?编译器应该如何决定选择要调用的方法?

I also tried to setup a static method on an interface a little while ago, not sure why now.不久前我还尝试在接口上设置静态方法,现在不知道为什么。 I did bookmark this so maybe it helps:我做了书签,所以也许它有帮助:

Interface with a static method by using extension methods 使用扩展方法与静态方法接口

If you're just after the type name, you can just do this:如果您只是在类型名称之后,则可以执行以下操作:

public class Helper
{
    static void PrintClassName<T>()
    {
         Console.WriteLine(typeof(T).Name);
    }
}

Declaring a static property , event or method on an interface definition is not considered a legal definition.在接口定义上声明static propertyeventmethod不被视为合法定义。 This is because interfaces are considered contracts and as such, represent something that will be implemented by every client instance of that interface.这是因为接口被认为是契约,因此,代表将由该接口的每个客户端实例实现的东西。

A static declaration essentially states that the static member does not require a physical client implementation in order to execute the required functionality and this falls short of the general concept of interfaces: providing a proven contract. static声明本质上声明static成员不需要物理客户端实现来执行所需的功能,这不符合接口的一般概念:提供经过验证的契约。

The answer is a qualified "not really but Sort Of".答案是一个合格的“不是真的,而是 Sort Of”。 You can provide a static extension method to all implementors of a given interface and can then call this from your implementer in a property or another method.您可以为给定接口的所有实现者提供静态扩展方法,然后可以从您的实现者在属性或其他方法中调用它。 As an example:举个例子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace InterfacesWithGenerics
{
    class Program
    {
        static void Main(string[] args)
        {
            Helper.PrintClassName<Example>(new Example());
            Console.ReadLine();
        }
    }

    public class Example : I
    {
        #region I Members

        public string ClassName
        {
            get { return this.GetClassName(); }
        }

        #endregion
    }

    public interface I
    {
        string ClassName { get; }
    }

    public class Helper
    {

        public static void PrintClassName<T>(T input) where T : I
        {           
            Console.WriteLine( input.GetClassName()) ;
        }
    }

    public static class IExtensions
    {
        public static string GetClassName(this I yourInterface)
        {
            return yourInterface.GetType().ToString();
        }
    }
}

Here we have an interface (I) which defines the property we care about and a static extension method (GetClassName) which is applied to all members of its type which does the grunt work of getting the information we want.这里我们有一个接口(I),它定义了我们关心的属性和一个静态扩展方法(GetClassName),它应用于其类型的所有成员,它完成了获取我们想要的信息的繁重工作。 We Have a Class (Example) which implements the I interface so when we call our static helper class passing in an instance of Example, it runs the static method against it.我们有一个实现 I 接口的类(示例),因此当我们调用传入示例实例的静态帮助器类时,它会针对它运行静态方法。 Unfortunately it is not valid to reference the type T directly within the method itself as a variable, you'll have to pass an instance into the application.不幸的是,直接在方法本身中将类型 T 作为变量引用是无效的,您必须将一个实例传递给应用程序。

You could define the className as attribute on the specific class.您可以将 className 定义为特定类的属性。 This is the preferred ay to store metadata in .net.这是在 .net 中存储元数据的首选方式。 This way you can query the attribute for the given class and you do not need an instance.通过这种方式,您可以查询给定类的属性,并且不需要实例。

Yes, you can - sort-of - if you don't mind defining new types that proxy instance calls to static methods:是的,您可以 - 有点 - 如果您不介意定义代理实例调用静态方法的新类型:

While an interface can only declare instance members, you can use a couple of simple tricks with C#'s generics, without needing reflection, to accomplish what you're after (and without resorting to Java-style AbstractFactoryBeanFactory design-pattern overuse).虽然interface只能声明实例成员,但您可以使用 C# 泛型的一些简单技巧,无需反射,即可完成您所追求的任务(并且无需过度使用 Java 风格的AbstractFactoryBeanFactory设计模式)。

What we can do, is define a separate struct (ie a value-type) that contains instance members that call-into the static members we want.我们可以做的是定义一个单独的struct (即值类型),其中包含调用我们想要的静态成员的实例成员。

So if we have this interface:所以如果我们有这个接口:

interface IStaticFunctionality
{
    void DoSomethingWithoutAnObjectInstance();
}

...and we want to do something like this: ...我们想做这样的事情:

void AGenericMethodThatDoesntHaveAnInstanceOfT<T>()
{
    T.DoSomethingWithoutAnObjectInstance();
}

...then we can do this: ...然后我们可以这样做:

void AGenericMethodThatDoesntHaveAnInstanceOfT<T>()
    where T : struct, IStaticFunctionality
{
    T t = default;
    t.DoSomethingWithoutAnObjectInstance();

    // Note the above code uses `T t default;` instead of `T t = new T()`.
    // This is because the C# compiler currently replaces `new T()` with `Activator.CreateInstance<T>()` in the generated bytecode.
    // This has poor performance compared to `default(T)` or a normal non-generic constructor call, but the compiler does this because it's a workaround for a design-bug back in C# 6.0: https://devblogs.microsoft.com/premier-developer/dissecting-the-new-constraint-in-c-a-perfect-example-of-a-leaky-abstraction/ 
}

So if we have different types with the static void DoSomethingWithoutAnObjectInstance method, we just need to define struct implementations of IStaticFunctionality for each of those types:因此,如果我们有不同类型的static void DoSomethingWithoutAnObjectInstance方法,我们只需要为每个类型定义IStaticFunctionality struct实现:

class Foo
{
    public static void DoSomethingWithoutAnObjectInstance()
    {
        Console.WriteLine("foo");
    }

    struct Static : IStaticFunctionality
    {
        void DoSomethingWithoutAnObjectInstance() => Foo.DoSomethingWithoutAnObjectInstance();
    }
}

class Bar
{
    public static void DoSomethingWithoutAnObjectInstance()
    {
        Console.WriteLine("bar");
    }

    struct Static : IStaticFunctionality
    {
        void DoSomethingWithoutAnObjectInstance() => Bar.DoSomethingWithoutAnObjectInstance();
    }
}

So then a call-site for AGenericMethodThatDoesntHaveAnInstanceOfT<Foo> would actually look like:那么AGenericMethodThatDoesntHaveAnInstanceOfT<Foo>的调用站点实际上看起来像:

AGenericMethodThatDoesntHaveAnInstanceOfT<Foo.Static>();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM