【问题标题】:Generic classes with methods that work only for some type parameters具有仅适用于某些类型参数的方法的泛型类
【发布时间】:2010-11-24 00:36:26
【问题描述】:

假设您正在编写一个库来在屏幕上显示内容,因此您创建了一个IDisplayable 接口。此接口有一种方法可以从对象创建控件:displayable.GetControl()

您想创建自己的可以显示的列表类型:MyList<T>。现在这个列表只有在TIDisplayable 时才能显示,所以你可以在 MyList 类中询问 T 应该实现 IDisplayable。但是当 T 不是 IDisplayable 时,您还希望在某些地方使用此列表类型(因此此列表将无法显示)。那么如果 T 实现了 IDisplayable,是否可以说 MyList 实现了 IDisplayable?如果MyList<T> 总是实现 IDisplayable 但如果您尝试调用 GetControl() 如果 T 不是 IDisplayable 时会在运行时抛出异常,我也会很高兴,但我想知道是否有一种静态类型安全的方法来做到这一点.这可以做到吗?还是我在寻找错误的解决方案?

编辑:

我同意到目前为止 MyList 可能有太多责任的建议。我最初的想法是创建一个MyDisplayableList<T> : MyList<T> (where T : IDisplayable)

这种方法的问题在于,我有很多方法接受 MyList 并返回 MyList(例如 Linq 中的 Select 等方法)。因此,如果我在 MyDisplayableList 上使用 select,我会返回一个 MyList,即使它是一个 MyList,我也无法显示它......在 C# 中是否有一种类型安全的方法来处理这个问题?

【问题讨论】:

  • 为什么你还想将 MyList 用于不可显示的类?在我看来,您要求 MyList 做太多事情,但没有原因很难说。

标签: c# generics types interface


【解决方案1】:

简单。检查类型是否为IDisplayable。如果不是,请发送InvalidOperationException

if (!typeof(IDisplayable).IsAssignableFrom(typeof(T))) 
    throw new InvalidOperationException();

或者,如果您有 T 的实例,只需检查一下:

IDisplayable disp = instanceOfT as IDisplayable;
if (disp == null)
    throw new InvalidOperationException();
// do stuff with `disp`.

您的设计可能有缺陷。你可能在课堂上放太多东西,违反了单一责任原则。首先重新检查您的设计。

【讨论】:

  • 谢谢,这就是我要使用的。但如果可用的话,我更喜欢静态类型安全的方法......
  • 没有办法静态地强制执行(对于类的某些特定方法)。类型约束在类型级别强制执行。
【解决方案2】:

如你所描述的,这是不可能的。您应该创建两种类型的列表:

public class MyList<T> : IList<T>
{
    ...
}

public class MyDisplayableList<T> : MyList<T> where T : IDisplayable
{
    ...
}

【讨论】:

  • 啊,是的,谢谢,这是我的第一个想法,但我没有告诉你足够的信息。这种方法的问题在于,我有很多方法采用 MyList 并返回 MyList (例如 Linq 中的 Select 等方法)。因此,如果我在 MyDisplayableList 上使用 select 我会返回一个 MyList 并且我无法显示它...在 C# 中是否有一种类型安全的方法来处理这个问题?
  • 然后将该方法设为通用:public TList Foo&lt;TList,TItem&gt;(TList list) where TList : MyList&lt;TItem&gt;
  • 谢谢。但是,我不知道这将如何解决问题。例如,如果我做了aMyList.Select((a) =&gt; a.ToDisplayable()),那么我将有一个MyList&lt;IDisplayable&gt;,但我将无法对其调用 GetControl(),因为它不是 MyDisplayableList。
  • System.Linq.Enumerable.Select 返回 IEnumerable&lt;...&gt; 而不是 MyList&lt;...&gt;
  • 是的,但我正在写一个不同的选择。 Linq 语法允许这样做。
【解决方案3】:

这里还需要泛型吗?

我猜你有多个实现IDisplayable 的类,并希望将它们的所有实例放在同一个列表中。所以你只需要一个

public class MyList : Collection<IDisplayable>, IDisplayable
{
    public void GetControl()
    {
        foreach (IDisplayable displayable in this)
        {
            displayable.GetControl();
        }
    }
}

如果你真的想将非IDisplayable 实例也放入该列表中,请找到公共基类并定义一个

public class MyList2 : Collection<object>, IDisplayable
{
    public void GetControl()
    {
        foreach (IDisplayable displayable in this.OfType<IDisplayable>())
        {
            displayable.GetControl();
        }
    }
}

【讨论】:

    【解决方案4】:

    我认为您希望MyList&lt;T&gt; 能够同时使用IDisplayable 和非IDisplayable 的原因是因为存在一些重复的功能。

    我建议您将基本实现设置为MyListBase&lt;T&gt;,它实现了两个列表都执行的基本功能。然后你有 MyDisplayableList 继承 MyList (MyDisplayableList&lt;T&gt; : MyList&lt;T&gt; where T : IDisplayable),它只执行特定于 IDisplayable 的功能。

    如果有任何特定于非 IDisplayble 的功能,请添加 NonDisplayableList&lt;T&gt; : MyListBase&lt;T&gt; 以执行这些功能。

    【讨论】:

      【解决方案5】:

      我认为解决此问题的更好方法是从列表中抽象出来并考虑IDisplayables 中的composite。这正是在 ASP.NET 或 Windows 窗体中建模控件的方式。

      public class CompositeDisplayable : IDisplayable {
      
        public void Add(IDisplayable displayable) {
          // TODO: check for null, cycles, etc...
          _list.Add(displayable);
        }
      
        public void Remove(IDisplayable displayable) {
          // TODO: check for null
          _list.Remove(displayable);
        }
      
        public Control GetControl() {
          var control = new Control(); 
          // this assumes Control is also a composite type
          _list.ForEach(displayable => control.Add(displayable.GetControl()));
          return control;
        }
      
        private List<IDisplayable> _list = new List<IDisplayable>();
      
      }
      

      然后,为了在您的“某物”集合与组合之间架起一座桥梁,您可以创建一个这样的扩展方法:

      public static class DisplayableExtensions {
      
        public static IDisplayable ToDisplayable(this IEnumerable source) {
          if (source == null) throw new NullReferenceException(); // extension method should behave like instance methods
          var result = new CompositeDisplayable();
          source.
            OfType<IDisplayable>().
            ToList().
            ForEach(displayable => result.Add(displayable));
          return result;
        }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多