Anyone has an alternative to using static methods

2019-08-20 05:37发布

I want to implement a collection, whose items need to be tested for emptiness. In case of a reference type, one would test for being null. For value types, one has to implement empty testing, and probably choose a specific value that represents emptyness.

My generic collection of T should be usable for both value and reference type values (meaning that Coll<MyCalss> and Coll<int> should both be possible). But I have to test reference and value types differently.

Wouldn't it be nice to have an interface that implements an IsEmpty() method, to exclude this logic from my generic type? But of course, this IsEmpty() method cannot be a member function: it could not be called on empty objects.

One workaround I found is to have collection items stored as objects, rather then T-s, but it gives me a headache (around boxing and being strongly typed). In good old C++ it was no problem :-)

The code below demonstrates what I'd like to achieve:

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

5条回答
We Are One
2楼-- · 2019-08-20 05:51

Instead of using an array in your class to represent the collection, use a stack or list. Then you don't have empty indexes, they simply aren't there if they where removed.

查看更多
倾城 Initia
3楼-- · 2019-08-20 05:52

How about just sending in two functions in the constructor?

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

You could then use those functions later:

if (!isEmpty(item))

Items[index] = createEmpty();
查看更多
冷血范
4楼-- · 2019-08-20 05:57

There is no such thing as an "empty" int, so supporting int will be tricky unless you store a bit-map of which are defined - but if you simply use a collection of int? (i.e. Nullable<int>) it is done for you. No extra work, and no boxing:

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

To get an int from an int?, any of:

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

And when you don't need to be able to test for empty, just a List<int> (no ?). No extra code. No special collection class. And it works for most anything.

查看更多
SAY GOODBYE
5楼-- · 2019-08-20 06:03

You can use the 'default' keyword for this, as in:

this.Items[Index] = default(T);
查看更多
男人必须洒脱
6楼-- · 2019-08-20 06:04

You can create an extension method for your Interface called IsEmpty. Then you could first test if the 'this' parameter in this extension method is null.

So you could call the IsEmpty method on any reference to a type that is implementing your interface, not regarding if this could be null or not.

查看更多
登录 后发表回答