使用IEnumerable或特定类型返回

问题描述

我正在尝试编写一个将给我HashSet集合的方法,然后我可以使用Contains()方法根据Contains()的时间复杂度来检查某些事物,因为HashSet为O(1)。这是一个简单的示例: 我写了两种方法,唯一的区别是返回类型。 GetNamesHashSet()返回特定的HashSet,GetNames()返回IEnumerable以使其通用。由于存在动态绑定,由于我在两个方法中都初始化了HaseSet变量,因此两者都将在运行时返回HashSet。在编译时,我们可以看到GetNames()应该返回IEnumerable。不过,我们可以声明一个变量aSet并调用HashSet从其他接口继承的其他方法。因此,两者都会给我们我们所需要的。由于用户无法使用该方法,因此他们从界面中知道的只是输出和输入。

我的问题是:

  1. 目的是通过调用GetNames()方法来使用HashSet的Contains()方法。一旦GetNames()方法将HashSet返回给调用方,我们就可以调用Contains()方法。那么哪种方式更适合这种情况?当我们编写GetNames()方法时返回IEnumerable或HashSet吗?

  2. 如果我返回IEnumerable(请参见下面的GetNames()方法代码示例),为什么我们可以通过在主方法中声明变量aSet来调用aSet.Contains(),因为它是动态绑定,所以一旦我们声明为var aset = person.GetNames(),集合的类型为IEnumerable,IEnumerable仅具有方法GetEnumerator(),为什么集合具有方法Contains()?


public class Program
{
    static void Main(string[] args)
    {
        var person = new Person() { Name = "a",Age = 20 };
        HashSet<string> namesSet = person.GetNamesHashSet();
        if (namesSet.Contains(person.Name)) 
        {
            Console.WriteLine(person.Age);
        }

        var aset = person.GetNames(); 
        if (aset.Contains(person.Name)) 
        {
            Console.WriteLine(person.Age);
        }

        Console.ReadLine();
    }
}

public interface IPerson
{
    IEnumerable<string> GetNames();
    HashSet<string> GetNamesHashSet();
}

public class Person :IPerson
{
    public string Name { get; set; }
    public int Age { get; set; }

    public IEnumerable<string> GetNames()
    {
        HashSet<string> set = new HashSet<string>();
        for (char c = 'a'; c < 'g'; c++)
        {
            set.Add(c.ToString());
        }
        return set;
    }

    public HashSet<string> GetNamesHashSet()
    {
        HashSet<string> hashSet = new HashSet<string>();
        for (char c = 'a'; c < 'g'; c++)
        {
            hashSet.Add(c.ToString());
        }
        return hashSet;
    }
}

解决方法

如果要将其暴露给外界,则理想的返回类型是ImmutableHashSet。它在.Net Core中可用,并且可以在带有NuGet包的.Net Framework中使用。如果您不能或不想添加新的依赖项(这是合理的),则返回NET::ERR_CERT_COMMON_NAME_INVALID.是下一个最佳选择。

在任何情况下效果都是相同的:您将返回最具体的类型,该类型不允许用户修改集合。这很重要,因为理想情况下,您希望缓存创建的集合,并在每次调用该方法时返回相同的对象,而不是每次都像当前那样重新生成它。 IReadOnlyCollection快是因为HashSet.Contains慢!

通过返回可写集合,您还暗示修改集合会以某种方式影响HashSet.Add对象,但事实并非如此。如果您返回只读内容,则您的API更清晰,更简单。

关于在PersonHashSet(或实际上IEnumerable)上调用“包含”,这是同一回事。在开始自己的测试之前,IReadOnlyCollection方法将检查源对象是否实现Enumerable.Contains并仅转发呼叫。