【问题标题】:Anyone has an alternative to using static methods in a C# interface?任何人都可以替代在 C# 接口中使用静态方法吗?
【发布时间】:2010-12-21 04:54:46
【问题描述】:

我想实现一个集合,它的项目需要进行空性测试。 在引用类型的情况下,将测试是否为空。对于值类型,必须实现空测试,并且可能选择一个表示空的特定值。

我的 T 通用集合应该可用于值和引用类型值(这意味着 Coll<MyCalss>Coll<int> 都应该是可能的)。但我必须以不同的方式测试引用和值类型。

拥有一个实现 IsEmpty() 方法的接口以从我的泛型类型中排除此逻辑不是很好吗?但是当然,这个 IsEmpty() 方法不能是成员函数:它不能在空对象上调用。

我发现的一种解决方法是将集合项存储为对象,而不是 T-s,但这让我很头疼(围绕装箱和强类型)。在好的旧 C++ 中没问题 :-)

下面的代码演示了我想要实现的目标:

using System;
using System.Collections.Generic;

namespace StaticMethodInInterfaceDemo
{
  public interface IEmpty<T>
  {
    static T GetEmpty();  // or static T Empty;

    static bool IsEmpty(T ItemToTest);
  }


  public class Coll<T> where T : IEmpty<T>
  {
    protected T[] Items;

    protected int Count;


    public Coll(int Capacity)
    {
      this.Items = new T[Capacity];
      this.Count = 0;
    }

    public void Remove(T ItemToRemove)
    {
      int Index = Find(ItemToRemove);

      // Problem spot 1: This throws a compiler error: "Cannot convert null to type parameter 'T'
      // because it could be a non-nullable value type. Consider using 'default(T)' instead."
      this.Items[Index] = null;

      // To overcome, I'd like to write this:
      this.Items[Index] = T.Empty;  // or T.GetEmpty(), whatever.

      this.Count--;
    }


    public T[] ToArray()
    {
      T[] ret = new T[this.Count];

      int TargetIndex = 0;
      for(int Index = 0; Index < this.Items.GetLength(0); Index++)
      {
        T Item = this.Items[Index];

        // Problem spot 2: This test is not correct for value types.
        if (Item != null)
          ret[TargetIndex++] = Item;

        // I'd like to do this:
        if (!T.IsEmpty(Item))
          ret[TargetIndex++] = Item;
      }

      return ret;
    }

    protected int Find(T ItemToFind)
    {
      return 1;  // Not implemented in the sample.
    }
  }
}

【问题讨论】:

标签: c# interface static methods


【解决方案1】:

您可以为您的接口创建一个名为 IsEmpty 的扩展方法。那么你可以先测试一下这个扩展方法中的'this'参数是否为null。

因此,您可以在对实现您的接口的类型的任何引用上调用 IsEmpty 方法,而不考虑这是否为 null。

【讨论】:

  • 这种方法的两个问题:1.)我没有提到我必须针对 .Net 2.0 进行编程(不支持扩展方法) 2.)最后,我需要接口在类声明的 wehre 子句中使用。这样我就不能告诉编译器 T 支持 .IsEmpty,可以吗?
  • 扩展方法是 .NET 2.0。它只是启用扩展方法语法的较新的 C# 编译器。您可以使用针对 2.0 的新编译器进行编译,并且仍然使用扩展方法。如果您将 Mono 中的 System.Core.dll 与您的应用程序一起部署,您甚至可以在 2.0 中使用 LINQ ;-) 当然,即使在通用约束中使用它也是如此。
  • 好吧,如果你这么说,我会试一试 :-) 谢谢。
【解决方案2】:

您可以为此使用“默认”关键字,如下所示:

this.Items[Index] = default(T);

【讨论】:

  • 有点好笑,因为在他的评论中编译器已经提出了这个建议。它有什么问题?
  • 问题是默认值可能是一个有效值并且不能确定“空”。
【解决方案3】:

没有“空”int 这样的东西,因此支持int 会很棘手,除非您存储已定义的位图 - 但如果您只是使用int?(即Nullable&lt;int&gt;)的集合为您完成。没有额外的工作,也没有拳击:

List<int?> list1 = ...; // data
List<string> list2 = ...; // data

Console.WriteLine(list1[3]; == null); // actually maps to `item.HasValue`
Console.WriteLine(list2[3]; == null); // reference test

要从int? 获取int,可以:

int i = (int)value; // throws exception if empty
int i = i.Value; // throws exception if empty
int i = i.GetValueOrDefault(); // returns 0 if empty

当您不需要测试是否为空时,只需一个List&lt;int&gt;(没有?)。没有额外的代码。没有特别的收藏类。它适用于大多数情况。

【讨论】:

  • 我喜欢它。不过,我需要创建我的集合(哈希表)。如何指定类型 T 必须是 Nullable 类型? "where T : Nullable" 不可用(当然,对于 int,我不需要它,但对于任何结构)
  • 你不能。没有涵盖 ref-type 和 Nullable&lt;T&gt; 的单一约束。
【解决方案4】:

不要在类中使用数组来表示集合,而是使用堆栈或列表。那么您就没有空索引,如果它们被删除,它们根本就不存在。

【讨论】:

  • 我需要空表示 :-)
【解决方案5】:

在构造函数中发送两个函数怎么样?

public Coll(Func<T> createEmpty, Func<T, bool> isEmpty)
{
    this.createEmpty = createEmpty;
    this.isEmpty = isEmpty;
}

您可以稍后使用这些功能:

if (!isEmpty(item))

Items[index] = createEmpty();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 2013-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-19
    相关资源
    最近更新 更多