【问题标题】:When to use a non-generic interface as a generic type constraint何时使用非泛型接口作为泛型类型约束
【发布时间】:2016-02-05 07:38:50
【问题描述】:

我正在努力寻找一种将非泛型接口用作泛型类型约束的场景。下面是一个任意示例,其中非泛型方法 (RideCar2) 比泛型方法 RideCar 更简单。

class Program
{
    static void Main(string[] args)
    {
        var car = new Merc();
        RideCar(car);
        RideCar2(car);
    }

    static void RideCar<T>(T t) where T : ICar
    {
        t.StartEngine();
        t.StopEngine();
    }

    static void RideCar2(ICar car)
    {
        car.StartEngine();
        car.StopEngine();
    }
}

public interface ICar
{
    void StartEngine();
    void StopEngine();
}

public class Merc : ICar
{
    public void StartEngine() { Console.WriteLine("Merc start"); }
    public void StopEngine() { Console.WriteLine("Merc stop"); }
}

很明显,RideCar2 是一个更好的实现,因为它噪音更小。

是否存在将非泛型接口用作泛型类型约束的场景?

更多示例(根据回复)

  1. 用作返回类型

static T RideCar(T t) where T : ICar
{
    t.StartEngine();
    t.StopEngine();
    return t;
}

使用普通方法仍然会导致使用泛型方法无用,见下文:


static ICar RideCar(ICar car)
{
 car.StartEngine();
 car.StopEngine();
 return car;
}
  1. 多个接口

static void RideCar(T t) where T : ICar, ICanTimeTravel
{
    t.StartEngine();      // ICar
    t.TravelToYear(1955); // ICanTimeTravel
    t.StopEngine();       // ICar
}

使用带有多个参数的普通方法仍然会导致使用泛型方法无用,见下文:


static void RideCar(ICar car, ICanTimeTravel canTimeTravel)
{
 car.StartEngine();
 canTimeTravel.TravelToYear(1955);
 car.StopEngine();
}

【问题讨论】:

  • 当你需要多个接口约束时
  • 看一个例子,泛型方法返回一个泛型类型的值。
  • @Damien_The_Unbeliever 对我来说这将是最好的答案
  • 您的示例static void RideCar(ICar car, ICanTimeTravel canTimeTravel) 的问题在于,以这种方式而不是使用通用约束没有任何收获。它使代码不太清晰,因为没有约束告诉调用者汽车和时间机器是同一个设备。

标签: c# generics interface


【解决方案1】:

是的,有。考虑:

static T RideCar<T>(T t) where T : ICar
{
    t.StartEngine();
    t.StopEngine();
    return t;
}

这将返回特定类型。现在您可以使用实现细节,而不必将其转换回实现类型,这是不好的做法。

此外,您可以对同一个泛型参数有多个接口约束:

static void RideCar<T>(T t) where T : ICar, ICanTimeTravel
{
    t.StartEngine();      // ICar
    t.TravelToYear(1955); // ICanTimeTravel
    t.StopEngine();       // ICar
}

最后,即使这有时被认为是代码异味,您也可以将new() 约束与您的接口约束一起使用,以便在方法内创建您的实现类型的实例:

static T Create<T>() where T : ICar, new()
{
    T t = new T();
    return t;
}

【讨论】:

  • 前 2 个示例仍然不需要通用方法(请参阅我对原始帖子的更新)。在第三个示例中,非泛型接口 (ICar) 用于约束创建有用的类型。
【解决方案2】:

这取决于。在您的情况下,首选非通用方式。

如果你想调用在不同接口上声明的方法,那么带有类型约束的泛型声明是有意义的:

public void Test<T>(T value, T other) 
    where T: IEquatable<T>, IComparable<T>
{
    value.Equals(other); //in IEquatable<T>
    value.CompareTo(other); //in IComparable<T>
}

但在这种情况下,如果可能的话,我更喜欢使用基类,并在没有任何类型约束的以下方法中使用它:

public class BaseCar<T> : IEquatable<T>, IComparable<T>, ICar
{
    /// [...]
}

public void Test<T>(BaseCar<T> car1, BaseCar<T> car2)
    where T: IEquatable<T>, IComparable<T>
{
    car1.Equals(car2);
    car1.CompareTo(car2);
}

或没有泛型但仅限于 ICar:

public class BaseCar : IEquatable<ICar>, IComparable<ICar>
{
    /// [...]
}

public void Test(BaseCar car1, BaseCar car2)
{
    car1.Equals(car2);
    car2.CompareTo(car2);
}

【讨论】:

  • IEquatable,IComparable 是通用接口。我问什么时候使用非泛型接口作为泛型方法的类型约束有用。
猜你喜欢
  • 1970-01-01
  • 2020-09-16
  • 2010-11-08
  • 1970-01-01
  • 2014-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多