【问题标题】:c# casting non generic implement of generic interfacec# 转换泛型接口的非泛型实现
【发布时间】:2023-04-04 07:35:01
【问题描述】:

我阅读了很多可能的答案,但没有找到适合我需要的答案!

我正在尝试声明一个泛型接口,具有不同的非泛型实现类,然后在相同的上下文中使用这些类。

这是我目前所拥有的:

接口

public interface WeirdType { }
public class WeirdType1 : WeirdType { }
public class WeirdType2 : WeirdType { }


public interface IGenericInterface<T>
{
  T GiveT();
  void TakeItBack(T data);
}

实现

public class NonGenericImplementation1 : IGenericInterface<WeirdType1>
{
  public WeirdType1 GiveT() { return new WeirdType1(); }
  public void TakeItBack(WeirdType1 data) { Console.WriteLine("Got it back 1"); }
}

public class NonGenericImplementation2 : IGenericInterface<WeirdType2>
{
  public WeirdType2 GiveT() { return new WeirdType2(); }
  public void TakeItBack(WeirdType2 data) { Console.WriteLine("Got it back 2"); }
}

用法

List<IGenericInterface<WeirdType>> implementations = new List<IGenericInterface<WeirdType>>
{
  new NonGenericImplementation1(),
  new NonGenericImplementation2()
};

foreach (var impl in implementations)
{
  WeirdType x = impl.GiveT();
  impl.TakeItBack(x);
}

这让我很震惊

'Argument Type NonGenericImplementation1 is not assignable to IGenericInterface<WeirdType>' 

即使一些黑色铸造魔法让我通过编译,它也会在运行时因同样的原因崩溃。

帮助我了解是什么让 C# 打字无法做到这一点和/或建议稍微复杂一点的模式以顺利完成

我可以通过很多不同的方式解决我的实际问题,所以完全替代的解决方案并不是我真正想要的

如果实际答案已经存在,很抱歉浪费时间

【问题讨论】:

  • 记录一下:当纯粹使用 getter 时,您可以通过将其声明为 public interface IGenericInterface&lt;out T&gt; 来使接口协变。列表实现将按原样被接受,但由于接受的答案中解释的原因,不允许使用 TakeItBack 函数(除非定义为例如 void TakeItBack(WeirdType data);
  • 我的每个实现本身都包含必要的输入信息!我的意思是,如果在我使用过程中可以使用 (impl.T) 之类的东西而不是 Weirdtype,那么从打字的角度来看,这应该可以工作

标签: c# generics theory typing


【解决方案1】:

问题是如果你从implementations 中取出一些东西,我们知道它是IGenericInterface&lt;WeirdType&gt; 类型的。这意味着我们应该能够调用GiveT 并获得WeirdTypeTakeItBack,同时传递WeirdType

这尤其意味着以下两项都必须有效:

impl.TakeItBack(new WeirdType1());
impl.TakeItBack(new WeirdType2());

现在如果implNonGenericImplementation1 类型,那么第二个无效,如果它是NonGenericImplementation2 类型,那么第一个无效。

这意味着我们不能声称NonGenericImplementation1IGenericInterface&lt;WeirdType&gt; 类型,NonGenericImplementation2 也是如此。

【讨论】:

  • 这是有道理的,这个例子很清楚为什么编译器不接受我的代码。
【解决方案2】:

与往常一样,如果没有上下文就很难做到中肯,但我仍会尽我所能提供帮助。

错误“Argument Type NonGenericImplementation1 is not assignable to IGenericInterface”对我来说似乎很好,在 C# 中,集合的不同成员必须具有相同的类型。

但是你为什么要声明:

public class NonGenericImplementation1 : IGenericInterface<WeirdType1>
public class NonGenericImplementation2 : IGenericInterface<WeirdType2>

代替:

public class NonGenericImplementation1 : IGenericInterface<WeirdType>
public class NonGenericImplementation2 : IGenericInterface<WeirdType>

这样,您的所有示例代码仍然可以工作,并且两个类将实现相同的接口,因此您现在可以声明:

List<IGenericInterface<WeirdType>> implementations = new List<IGenericInterface<WeirdType>>
            {
              new NonGenericImplementation1(),
              new NonGenericImplementation2()
            };

希望对你有帮助。

【讨论】:

    【解决方案3】:

    IGenericInterface&lt;WeirdType1&gt; 不是 IGenericInterface&lt;WeirdType&gt;,即使 WeirdType1 实现了 WeirdType

    【讨论】:

      【解决方案4】:

      您没有任何实现 IGenericInterface 的非泛型类。

      您的“实现”集合只能接受实现上述接口的类型的对象。

      这就是泛型的美妙之处。它们是强类型的,特别是当您处理泛型的泛型时,它变得更具挑战性。但是没有办法。

      首先,Generic List 不会接受任何未实现 IGenericInterface 的类型的对象。接下来,当您拥有这种类时,它也不允许使用除“WeirdType”以外的类型实例化的对象,即使该类型是从“WeirdType”继承的。

      因此,泛型中的继承在第一级有所帮助,但仅此而已。

      您的问题有解决方案。这不是一个 100% 有效的解决方案,许多 OOP 专家不会同意这一点,但它具有您想要的类结构,并且可以按照您想要的方式工作。

      您需要创建如下子类。

      public class GenericClass<T> : IGenericInterface<T> where T : WeirdType
      {
          public virtual T GiveT()
          {
              return default(T);
          }
      
          public virtual void TakeItBack(T data)
          {
      
          }
      }
      
      public class NonGenericClass1 : GenericClass<WeirdType1>, IGenericInterface<WeirdType>
      {
          public override WeirdType1 GiveT()
          {
              return new WeirdType1();
          }
      
          public void TakeItBack(WeirdType data)
          {
              Console.WriteLine("Got it back 1");
          }
      
          public override void TakeItBack(WeirdType1 data)
          {
              this.TakeItBack(data);
          }
      
          WeirdType IGenericInterface<WeirdType>.GiveT()
          {
              return GiveT();
          }
      }
      
      public class NonGenericClass2 : GenericClass<WeirdType2>, IGenericInterface<WeirdType>
      {
          public WeirdType2 GiveT()
          {
              return new WeirdType2();
          }
      
          public void TakeItBack(WeirdType data)
          {
              Console.WriteLine("Got it back 2");
      
          }
      
          public override void TakeItBack(WeirdType2 data)
          {
              this.TakeItBack(data);
          }
      
          WeirdType IGenericInterface<WeirdType>.GiveT()
          {
              return this.GiveT();
          }
      }
      

      现在你可以初始化通用接口的集合,并在其中添加子类的对象。

      var obj1 = new NonGenericClass1();
              var obj2 = new NonGenericClass2();
              List<IGenericInterface<WeirdType>> implementations = new List<IGenericInterface<WeirdType>>
              {
                 obj1, obj2
              };
      
              foreach (var impl in implementations)
              {
                  WeirdType x = impl.GiveT();
                  impl.TakeItBack(x);
              }
      

      这应该可以解决您的问题。

      【讨论】:

        【解决方案5】:

        感谢大家的意见,我试图不重复与 T 无关的代码,尽可能少的样板代码。

        我采用的解决方案是添加一个实现额外接口的抽象类。

        这不令我满意,因为我相信您应该能够为泛型编写与类型无关的代码,因此无需修改实际的类和接口代码/层次结构,也不会滥用反射。

        这可以通过扩展方法和非类型化的泛型列表实现,后者在 C# 中不受支持。

        这是额外的代码:

        public interface IComplexable { void DoComplexStuff(); }
        
        public abstract class GenericImplementation<T> : IGenericInterface<T>, IComplexable
        {
            public abstract T GiveT();
            public abstract void TakeItBack(T data);
        
            public void DoComplexStuff()
            {
                T x = GiveT();
                TakeItBack(x);
            }
        }
        

        类声明:

        public class NonGenericImplementation1 : GenericImplementation<WeirdType1>
        public class NonGenericImplementation2 : GenericImplementation<WeirdType2>
        

        用法:

        var impl = new IComplexable[] { new NonGenericImplementation1(),new NonGenericImplementation2() };
        foreach (var complexable in impl)
        {
          complexable.DoComplexStuff();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-19
          • 2020-03-02
          相关资源
          最近更新 更多