【问题标题】:C# interface static method call with generics使用泛型调用 C# 接口静态方法
【发布时间】:2010-11-17 15:40:05
【问题描述】:

有没有一种简单的方法来实现这一点,如果可能的话不实例化一个对象:

interface I
{
     static  string GetClassName();
}

public class Helper
{

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

【问题讨论】:

  • 你的接口实现是什么样的?
  • 鉴于接口定义了实例成员,您希望如何在没有实例的情况下调用接口成员?如果您想要类名,请使用 typeof(T) 并转储接口。
  • 你想在这里实现什么?确定typeof(T).FullName 会完成这项工作吗?
  • GetClassName 只是为了举例......

标签: c# generics inheritance interface static


【解决方案1】:

改用扩展方法:

public interface IMyInterface
{
     string GetClassName();
}

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

这允许您添加静态扩展/实用方法,但您仍然需要 IMyInterface 实现的实例。

你不能有静态方法的接口,因为它没有意义,它们是没有实例的实用方法,因此它们实际上没有类型。

【讨论】:

  • 好的,但是你认为我们可以避免输入参数吗?假设我们有一种用于接口或其他方式的单例模式?
  • 请注意您的意思——您可以将此扩展方法称为实例方法:new MyImplementation().PrintClassName()
  • 非常感谢您的回答——这是添加针对特定接口的“util”方法的绝妙方法。我还推荐有关扩展方法的 MSDN 文档(下面的链接)。请注意,MSDN 示例适当地命名了扩展方法,以便调用主体必须明确包含扩展。扩展方法:msdn.microsoft.com/en-us/library/bb383977.aspx
【解决方案2】:

您不能继承静态方法。您的代码不会以任何方式编译,因为接口因此不能有静态方法。

引用自littleguru:

.NET 中的继承仅适用于 实例库。静态方法是 在类型级别上定义而不是在 实例级别。这就是为什么覆盖 不适用于静态 方法/属性/事件...

静态方法只在 记忆。没有虚拟表等。 那是为他们创造的。

如果你调用一个实例方法 .NET,你总是给它当前的 实例。这被 .NET 隐藏了 运行时,但它发生了。每个实例 方法有一个指针作为第一个参数 (引用)对对象的 方法运行。这不会发生 使用静态方法(因为它们是 在类型级别定义)。 应该怎么做 编译器决定选择 调用方法?

【讨论】:

  • 肯定编译器不能选择(或者如果他可以选择,因为我们用正确的类显式调用了正确的静态 focntion)。但是,我们可以用通用的方法解决这个问题吗?
  • 或许有可能。但是选择这种设计是为了让它更加简单和干净。
【解决方案3】:

不久前我还尝试在接口上设置静态方法,现在不知道为什么。我确实为它添加了书签,所以也许它会有所帮助:

Interface with a static method by using extension methods

【讨论】:

    【解决方案4】:

    如果你只是在类型名称之后,你可以这样做:

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

    【讨论】:

    • 不错的答案 (+1) 但我要补充一点,你不应该真的这样做 - 如果你所做的只是传递一个类型,你应该使用常规参数:PrintClassName(Type input ) { Console.WriteLine(input.Name); }
    【解决方案5】:

    在接口定义上声明staticpropertyeventmethod 不被视为合法定义。这是因为接口被认为是契约,因此,代表了由该接口的每个客户端实例实现的东西。

    static 声明实质上表明static 成员不需要物理客户端实现即可执行所需功能,这不符合接口的一般概念:提供经过验证的合约。

    【讨论】:

      【解决方案6】:

      答案是一个合格的“不是真的,而是排序”。您可以为给定接口的所有实现者提供静态扩展方法,然后可以在属性或其他方法中从您的实现者调用它。举个例子:

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

      这里我们有一个接口 (I),它定义了我们关心的属性和一个静态扩展方法 (GetClassName),它应用于其类型的所有成员,它完成了获取我们想要的信息的繁重工作。我们有一个实现 I 接口的类(Example),所以当我们调用静态帮助类时传入一个 Example 的实例,它会针对它运行静态方法。不幸的是,直接在方法本身作为变量引用类型 T 是无效的,您必须将实例传递给应用程序。

      【讨论】:

        【解决方案7】:

        您可以将 className 定义为特定类的属性。这是在 .net 中存储元数据的首选方式。这样您就可以查询给定类的属性,并且不需要实例。

        【讨论】:

          【解决方案8】:

          是的,如果您不介意定义代理实例调用静态方法的新类型,您可以 - 某种程度上说:

          虽然interface 只能声明 instance 成员,但您可以使用 C# 的泛型使用几个简单的技巧,无需反射,即可完成您所追求的(并且无需求助于 Java -style AbstractFactoryBeanFactory 设计模式过度使用)。

          我们可以做的是定义一个单独的struct(即一个值类型),其中包含调用我们想要的静态成员的实例成员。

          所以如果我们有这个接口:

          interface IStaticFunctionality
          {
              void DoSomethingWithoutAnObjectInstance();
          }
          

          ...我们想做这样的事情:

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

          ...那么我们可以这样做:

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

          因此,如果我们使用 static void DoSomethingWithoutAnObjectInstance 方法有不同的类型,我们只需要为每个类型定义 structIStaticFunctionality 实现:

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

          那么AGenericMethodThatDoesntHaveAnInstanceOfT&lt;Foo&gt; 的呼叫站点实际上看起来像:

          AGenericMethodThatDoesntHaveAnInstanceOfT<Foo.Static>();
          

          【讨论】:

            猜你喜欢
            • 2011-07-14
            • 2016-12-14
            • 1970-01-01
            • 2018-10-30
            • 2015-03-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-04-25
            相关资源
            最近更新 更多