【问题标题】:Inherit from a generic base class, apply a constraint, and implement an interface in C#从泛型基类继承,应用约束,并在 C# 中实现接口
【发布时间】:2011-01-01 17:18:16
【问题描述】:

这是一个语法问题。我有一个泛型类,它继承自泛型基类,并对其中一个类型参数应用约束。我还希望派生类实现一个接口。在我的一生中,我似乎无法弄清楚正确的语法。

这就是我所拥有的:

DerivedFoo<T1,T2> : ParentFoo<T1, T2> where T2 : IBar { ... }

首先想到的是:

DerivedFoo<T1,T2> : ParentFoo<T1, T2> where T2 : IBar, IFoo { ... }

但这是不正确的,因为这导致 T2 需要同时实现 IBar 和 IFoo,而不是 DerivedFoo 来实现 IFoo。

我已经尝试了一些谷歌搜索,使用冒号、分号等,但我发现很短。我敢肯定,答案非常简单。

【问题讨论】:

  • 我看一次时无法理解@Adam 的答案,但 2 分钟后我明白了它是什么,谢谢你的回答。派生类有不止一个实现可能就是这一点。无论如何,我想向其他人展示它的符号。 “类 DerivedClass : ParentClass where Type : IType”。最后实现的类和 where 子句之间不应有任何内容。

标签: c# generics inheritance interface constraints


【解决方案1】:

在定义通用约束之前,您应包含类的整个签名。

class DerivedFoo<T1, T2> : ParentFoo<T1, T2>, IFoo where T2 : IBar
{
    ...
}

【讨论】:

  • 对其他人来说,我已将其内化为,一个类只有一个 where 子句,并且它在所有通用类型约束的末尾都有。
  • @Visser 允许有多个where子句,类Test where T1 : Interface1 where T2 : Interface2
  • @Visser 是的,bwing 说的是,每个 where 子句也可以有多个约束......所以原始帖子的语法是正确的,它只是意味着操作想要的不同。 where T2 : IBar, IFoo 只是意味着T2 必须实现这两个接口而不是DerivedFoo&lt;T1,T2&gt; 实现IFoo
【解决方案2】:

我的建议:当您对 C# 语言的语法有疑问时,请阅读规范;这就是我们发布它的原因。您需要阅读第 10.1 节。

为了回答您的具体问题,类声明中的顺序是:

  • 方括号中的属性
  • 修饰符(“public”、“static”等)
  • “部分”
  • “类”
  • 类名
  • 尖括号内类型参数声明的逗号分隔列表
  • 冒号后跟以逗号分隔的基类型列表(基类和实现的接口,如果有基类,则必须在前)
  • 类型参数约束
  • 类的主体,用大括号括起来
  • 分号

该列表中的所有内容都是可选的,除了“类”、名称和正文,但如果出现,则所有内容都必须按该顺序出现。

【讨论】:

  • Eric,虽然我非常尊重您作为专业人士并感谢您的反馈,但我不禁对粗鲁的回答感到沮丧。您批评我选择在编程问答网站上提出问题,而不是通过 MSDN 中的链接隐藏的技术含量高的 503 页 Word 文档进行定位、下载和搜索。那是相当粗糙。这是对我时间的最有效利用,并且还有一个额外的好处,那就是它可以在以后帮助其他人。有兴趣的 C# 语言规范的链接是:msdn.microsoft.com/en-us/vcsharp/aa336809.aspx
  • 无意批评。纯文本交流中普遍存在偏见,这使得简单的事实陈述听起来很粗暴和粗暴;当我收到一份有用的事实清单时,我会尝试善意地阅读,并建议您也这样做。我支持我的建议;如果您对语法有任何疑问,规范会给出明确的答案,并从一个有用的目录开始,用于查找特定语法的定义。
  • Dan,查找 C# 规范就像在 Google 中输入“C# Spec”并点击“我很幸运”按钮一样简单。如果您是专业的 C# 开发人员,您的机器上应该已经有 PDF 格式的 C# 规范。另外,我也不是要批评你。我之前不习惯阅读规范,但感谢 Jon、Eric 和 Pavel,他们总是在任何问题中引用 C# 规范。我发现 C# 规范,尽管有时可能很难阅读,但它是学习该语言的好方法。
  • @Eric Lippert:很公平。感谢你的回复。作为一个建设性的建议,如果 Microsoft 将规范的内容直接集成到 MSDN 中,并将其作为单独的下载存在,将会很有帮助。 Visual Studio .Net MSDN 版本具有规范的集成版本,但没有更高版本。我曾考虑购买 Anders Hejlberg 的书,但 .Net 4.0 即将到来,我还不愿意购买。 amazon.com/C-Programming-Language-3rd/dp/0321562992 谢谢。
  • C++ 要求类声明以分号结尾。许多 C# 开发人员来自 C++ 背景;有时他们的手指在没有大脑参与的情况下输入分号。 :-) C# 中有许多结构,它们采用可选的半,而 C++ 需要一个。这几乎只是一种微妙的便利。我想它还可以让您巧妙地在类型声明结束时调用方法体声明。
【解决方案3】:
public interface IFoo {}
public interface IBar {}

public class ParentFoo<T,T1> { }
public class DerivedFoo<T, T1> : ParentFoo<T, T1>, IFoo where T1 : IBar { }

【讨论】:

    【解决方案4】:
    public class KeyAndValue<T>
    {
        public string Key { get; set; }
        public virtual T Value { get; set; }
    }
    
    public class KeyAndValue : KeyAndValue<string>
    {
        public override string Value { get; set; }
    }
    

    这是对现有答案的扩展。如果您不提供类型,则默认为string。我没有实现一个接口,但这不应该需要任何与平常不同的东西。

    【讨论】:

      【解决方案5】:

      通用继承规则

      在 C# 中继承泛型类型类时,您需要遵循额外的规则。请记住,您的基类和派生类可以使用不同的泛型类型或共享它们。以下测试帮助我理解了规则。泛型类可以从具体、封闭构造或开放构造的基类继承,如下所示:

      // First create some Base Classes to inherit from.
      // Non-generic and generic classes are included.
      // Below, I will inherit these in Derived Classes.
      // If my inheritance test works, you will see "YES", otherwise "NO".
      
      class BaseType1 { }
      class BaseType2<T> { }
      class BaseType3<T1,T2> { }
      
      // -------------------------------------------------
      
      // Concrete type inheritance. Here the inheriting type does NOT have to be generic as the child
      
      class Derived1 : BaseType1 { }// YES!
      class Derived2<T> : BaseType1 { }// YES!
      
      // Open constructed type generic inheritance allows shared generic type inheritance.
      class Derived3<T> : BaseType2<T> { }// YES!
      
      // Closed constructed type inheritance. Note that what the child class accepts as far as types is different from what the parent accepts. This is allowed allowed as each implements its own types and constraints on its members and the parent has defined its type.
      class Derived4<T> : BaseType2<int> { }// YES!
      
      // NO! Base type must know its accepted type if the child class is not accepting a generic "T" type as well.
      //class Derived5 : BaseType2<T> { }// NO!
      
      // Another form that limits generic type. Note, here the first base type uses a reference type "string" that is different from what the derived child type uses, which is any "struct" type or int, etc. Because the base type is defined as "string" the child can define anything for its generic or not even use a generic.
      
      // The "where" clause only affects the derived class type!
      class Derived6<T> : BaseType2<T> where T : struct { }// YES!
      class Derived7<T> : BaseType2<string> where T : struct { }// YES!
      
      // NO! The "where" clause cannot be used to define the base class's type!
      //class Derived8 : BaseType2<T> where T : struct { }// NO!
      
      
      // NO! "T1" and "T2"must be a concrete type again if the derived type doesn't use the same
      //class Derived9 : BaseType3<T1,T2>// NO!
      
      // Here, both parent and child classes must accept one generic type for this to work.
      class Derived10<T> : BaseType3<T,T> { }// YES!
      
      // Each generic type must have unique names in the derived class AND match the base class names. That is why these fail.
      //class Derived11<T, T> : BaseType3<T1, T2> { }// NO!
      //class Derived12<name1, name2> : BaseType3<T1, T2> { }// NO!
      
      class Derived13<T1, T2> : BaseType3<T1, T2> { }// YES!
      
      // As long as one generic name matches between child and parent, this shared generic type on child and parent ok.
      class Derived14<Tname,T2> : BaseType2<Tname> { }// YES!
      
      // Again, the child class doesn't have to be generic if the base generic class has defined the types it accepts.
      class Derived15 : BaseType3<string, int> { }// YES!
      
      class Derived16<T1, T2> : BaseType3<string, int> { }// YES!
      
      // Here, you can have the child class accept two generic types, while the parent uses its own explicit type.
      class Derived17<T1, T2> : BaseType2<string> { }// YES!
      
      // No, you need to define these using "where".
      //class Derived18<string, int> : BaseType1 { }// NO!
      
      class Derived19<T1, T2> : BaseType1 where T1 : class where T2 : struct { }// YES!
      
      // This generates an error. You cannot inherit a generic type.
      // class Derived20 : T {}// NO!
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-11
        • 2020-03-02
        • 1970-01-01
        • 2019-03-22
        相关资源
        最近更新 更多