【问题标题】:List<T<U>> in c# where T and U are interfacesC#中的List<T<U>>,其中T和U是接口
【发布时间】:2012-10-15 17:30:24
【问题描述】:

好的。所以我目前正在尝试在 C# 中创建一个以接口为参数的接口列表。为了更清楚,让我举个例子:

public interface IPlate<T> where T : IWaffle {}
public interface IWaffles {}

public class BelgiumWaffle : IWaffle {}
public class FalafelWaffle : IWaffle {}
public class HugePlate : IPlate<BelgiumWaffle> {}
public class SmallPlate : IPlate<FalafelWaffle> {}


// Now, I want to do the following:
var plates = new List<IPlate<IWaffle>>();
plates.add(new HugePlate());
plates.add(new SmallPlate());

目标是能够将 IPlate 对象列表序列化为 XML。我希望使用泛型来做到这一点,但我不断收到错误消息,告诉我在尝试添加时存在一些参数错误(又名 - 类型不匹配)。只是不确定我在这里做错了什么。这对我来说似乎是正确的,但我一定是遗漏了一些东西(显然)。

更新:我应该提到这是.NET v3.5

更新:对不起!写关于板类定义的问题时,有些错别字。

【问题讨论】:

  • IWaffle有什么属性吗?是[可序列化]。盘子呢?
  • 您的示例中没有IPancake 的定义。我猜你的意思是IWaffle
  • List> 应该是 List> 吗?还有哪个 .Net 版本?
  • 这不是有效的 C# - 请编辑。 1) public class HugePlate, 2) IPancake 是从哪里来的?
  • Hmm HugePlate 和 SmallPlate 似乎没有实现 IPlate...typo?

标签: c# .net generics .net-3.5


【解决方案1】:

我认为你应该使用协方差(.NET 4.0)

public interface IPlate<out T> where T : IWaffle {}

并将IPancake 替换为IWaffle

var plates = new List<IPlate<IPancake>>();

【讨论】:

  • 抱歉,混淆了我正在使用的代码中的示例(已更改为 IWaffles
  • 这真的很酷。您是否碰巧知道如何使用 v3.5 实现相同的目标?不幸的是,这就是我们现在所坚持的版本。
  • 定义一个非泛型IPlate 接口作为泛型接口的基础并使用List&lt;IPlate&gt;。我不认为你可以在 3.5 中实现更多的类型安全
【解决方案2】:

HugePlateSmallPlate 都没有实现 IPlate&lt;IPancake&gt; 列表所需的 IPlate&lt;IPancake&gt; 接口。

【讨论】:

  • 对不起,混淆了。已经更新。 (这不应该是评论吗?)
  • @John 我仍然看到HugePlate&lt;BelgiumWaffle&gt;(这是不正确的,因为BelgiumWaffle 是一个类名,应该有一个变量名),并且在该行中没有IPlate&lt;IWaffle&gt;(也没有下一个) .
  • @Protron 为什么HugePlate&lt;BelgiumWaffle&gt; 不正确?它为我的测试编译并运行?鉴于HugePlate 继承自IPlate&lt;T&gt; where T : IWaffle 并且BelgiumWaffle 属于IWaffle,对我来说似乎也很合适
  • @Protron 我应该在我的类声明中使用变量名吗?真的吗??
  • @John 在您的原始声明中,该部分未编译。 BelgiumWaffle 这个词在类名声明的通用部分中。现在你把它移到继承的泛型部分(没关系)。通过变量名,我的意思是您的 IPlate 声明中的 T(如果这是您的要求)。
【解决方案3】:

除了协方差(@JakubKonecki 已经指出),您对 HugePlate 和 SmallPlate 的定义看起来不正确,因为它们需要实现 IPlate

试试这个:

public interface IPlate<out T> where T : IWaffle {}
public interface IWaffle {}

public class BelgiumWaffle : IWaffle {}
public class FalafelWaffle : IWaffle {}
public class HugePlate<T> : IPlate<T> where T : IWaffle {}
public class SmallPlate<T> : IPlate<T> where T : IWaffle {}

【讨论】:

  • 嘿,你说得对,我的问题确实有错误(错字,对不起!),我已经更新了。但是,我拥有的类定义(出于我的目的)需要继续定义 T 以实现 IPlate。
  • 所以之后你只需要将out 放入IPlate 的定义中:)
  • @John 我不是在吹毛求疵,我知道这是一个人为的例子,只是为了展示你的问题......只是我不知道你在你的例子中打错了哪些部分,哪些是您真实代码中的实际问题:)
【解决方案4】:

在 3.5 中工作,感谢@JakubKonecki 指出协方差

    public interface IWaffle { }
    public interface IPlate<out T> where T : IWaffle { }
    public interface IPancake : IWaffle { }

    public class BelgiumWaffle : IWaffle {}
    public class FalafelWaffle : IWaffle {}
    public class HugePlate : IPlate<BelgiumWaffle> {}
    public class SmallPlate : IPlate<FalafelWaffle> { }

    var plates = new List<IPlate<IWaffle>>();
    plates.Add(new HugePlate());
    plates.Add(new SmallPlate());

【讨论】:

  • 我认为这行得通。不过,在我的真实代码中,我遇到了错误,因为我在接口中定义了一个返回 List 的属性,并且它抱怨 T 是一个变体类型......嗯......
  • 在 3.5 中不起作用。 .NET Framework 4 中添加了用于协方差的 out 关键字。
【解决方案5】:

你可以为 T 使用抽象类而不是接口吗?

public abstract class Waffle { }
public interface IPlate<T> where T : Waffle
{
    T Food
    {
        get;
        set;
    }
}

public class BelgiumWaffle : Waffle { }
public class FalafelWaffle : Waffle { }
public class HugePlate<T> : IPlate<T> where T : Waffle
{
    public HugePlate(T food)
    {
        this.Food = food;
    }

    public T Food
    {
        get;
        set;
    }
}

public class SmallPlate<T> : IPlate<T> where T : Waffle
{
    public SmallPlate(T food)
    {
        this.Food = food;
    }

    public T Food
    {
        get;
        set;
    }
}

public class Test
{
    Test()
    {
        var platesOfWaffle = new List<IPlate<Waffle>>();
        platesOfWaffle.Add(new HugePlate<Waffle>(new BelgiumWaffle()));
        platesOfWaffle.Add(new SmallPlate<Waffle>(new FalafelWaffle()));
    }
}

【讨论】:

    【解决方案6】:

    在 .NET Framework 3.5 中,您没有像在 .NET Framework 4.0 中那样在泛型协方差上使用 out 参数。

    您可以尝试使用 IPlate 的非通用版本来解决它(在这种情况下,我将其命名为 IPlateNG)。

    考虑 .NET Framework 4.0 中的以下示例(我不得不将其展开以表明我的观点):

    using System;
    using System.Collections.Generic;
    
    public interface IWaffle { string Eat(); }
    // on C# 4.0 you just put the "out" to mark the covariance (and that is it)
    public interface IPlate<out T> where T : IWaffle { T GetMyWaffle(); }
    
    public class BelgiumWaffle : IWaffle {
        public string Eat() { return "Eating a Belgium Waffle"; }
        public string Breakfast() { return "Breakfasting a Belgium Waffle"; }
    }
    public class FalafelWaffle : IWaffle {
        public string Eat() { return "Eating a Falafel Waffle"; }
        public string Dinner() { return "Having dinner with a Falafel Waffle"; }
    }
    public class HugePlate : IPlate<BelgiumWaffle> {
        public BelgiumWaffle GetMyWaffle() { return new BelgiumWaffle(); }
    }
    public class SmallPlate : IPlate<FalafelWaffle> {
        public FalafelWaffle GetMyWaffle() { return new FalafelWaffle(); }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var plates = new List<IPlate<IWaffle>>();
            plates.Add(new HugePlate());
            plates.Add(new SmallPlate());
    
            IPlate<IWaffle> aPlate = plates[0];
            // Anyway, when you get a member of the collection you'll get the interface, not a concrete class (obviously).
            IWaffle aWaffle = aPlate.GetMyWaffle();
            // So you cannot invoke any specifics (like Breakfast or Dinner)
            Console.WriteLine(aWaffle.Eat());
    
            // But if you cast the member of the collection to the specific class (or interface)
            IPlate<FalafelWaffle> aSmallPlate = (SmallPlate)plates[1];
            // Then you'll get the concrete class without casting again
            FalafelWaffle aFalafel = aSmallPlate.GetMyWaffle();
            Console.WriteLine(aFalafel.Dinner());
        }
    }
    

    现在 .NET Framework 3.5 也是如此:

    using System;
    using System.Collections.Generic;
    
    public interface IWaffle { string Eat(); }
    // In this case I define this extra inteface which is non-generic
    // And inside it, we need a new method equivalent to the one on the generic one
    public interface IPlateNG { IWaffle GetWaffle(); }
    // And make the generic one implement the non-generic one
    public interface IPlate<T> : IPlateNG where T : IWaffle { T GetMyWaffle(); }
    
    public class BelgiumWaffle : IWaffle {
        public string Eat() { return "Eating a Belgium Waffle"; }
        public string Breakfast() { return "Breakfasting a Belgium Waffle"; }
    }
    public class FalafelWaffle : IWaffle {
        public string Eat() { return "Eating a Falafel Waffle"; }
        public string Dinner() { return "Having dinner with a Falafel Waffle"; }
    }
    public class HugePlate : IPlate<BelgiumWaffle> {
        // This extra method is needed due the lack of the 'out' on the definition
        public IWaffle GetWaffle() { return GetMyWaffle(); }
        public BelgiumWaffle GetMyWaffle() { return new BelgiumWaffle(); }
    }
    public class SmallPlate : IPlate<FalafelWaffle> {
        // This extra method is needed due the lack of the 'out' on the definition
        public IWaffle GetWaffle() { return GetMyWaffle(); }
        public FalafelWaffle GetMyWaffle() { return new FalafelWaffle(); }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            // The list cannot work with the IPlate<IWaffle> anymore. So here comes IPlateNG to the rescue
            var plates = new List<IPlateNG>();
            plates.Add(new HugePlate());
            plates.Add(new SmallPlate());
    
            IPlateNG aPlate = plates[0];
            // And instead of calling to the GetMyWaffle method we can call to the GetWaffle in this case
            IWaffle aWaffle = aPlate.GetWaffle();
            Console.WriteLine(aWaffle.Eat());
    
            IPlate<FalafelWaffle> aSmallPlate = (SmallPlate)plates[1];
            FalafelWaffle aFalafel = aSmallPlate.GetMyWaffle();
            Console.WriteLine(aFalafel.Dinner());
        }
    }
    

    请注意,我必须在 IPlate 两个具体类上制作 GetMyWaffle(命名为 GetWaffle)的额外非泛型版本,以解决缺少“out”关键字的问题.但其余部分非常相似。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-30
      • 2019-01-26
      相关资源
      最近更新 更多