是否可以将传递给.NET方法的变量类型限制为不是派生类?

问题描述 投票:1回答:3

我能否以一种在编译而不是运行时捕获此类错误的方式约束传递给我的方法的类型?

我当前的代码如下所示:

void Main()
{
    var dog = new Dog();
    SaveAnimal(dog);
}

void SaveAnimal(Animal animal) {
    var isAnimal = animal.GetType().UnderlyingSystemType == typeof(Animal);
    Debug.Assert(isAnimal, "You can not save dogs!");
}

class Animal {
    public int Legs { get; set; }
}

class Dog : Animal {
    public int TailLength { get; set; }
}
c# .net
3个回答
6
投票

不,语言中没有办法静态地将其视为使用错误。

您可以在运行时断言,因为您已经这样做了。然而,这违背了继承的精神,因为一个基本假设是派生类型必须替代基类型(Liskov替换原则)。

也许你可以通过给Animal一个新的方法abstract void Save()让动物自救。然后,每只动物决定做什么。然后Dog可以扔一个NotSupportedException


2
投票

是的,但只有使用泛型和接口的解决方法。

您需要做的是声明3个接口

public interface ISaveAbleBase { }
public interface ISaveAble : ISaveAbleBase{ }
public interface INotSaveAble : ISaveAbleBase { }

现在,您需要为Animal类提供一个通用参数,并将其约束为ISaveAbleBase类型。

class Animal<T> where T: ISaveAbleBase
{
    public int Legs { get; set; }
}

这样,您现在可以在派生类中指定它们是否可以保存:

class Dog : Animal<INotSaveAble> 
{
    public int TailLength { get; set; }
}

然后,您可以使您的方法通用,并仅将类型约束到可以保存的动物

void SaveAnimal<T>(T animal) where T: Animal<ISaveAble>

现在结果看起来如下:

void Main()
{
    var dog = new Dog();
    SaveAnimal(dog); // does not compile

    Animal<ISaveAble> generalAnimal = new Animal<ISaveAble>();
    SaveAnimal(generalAnimal); // compiles      
}

免责声明:此构造还允许您拥有无法保存的一般Animal

Animal<INotSaveAble> generalAnimalNotSave = new Animal<INotSaveAble>();
SaveAnimal(generalAnimalNotSave); // does not compile

PS。这个答案的灵感来自this post


1
投票

没有标准的方法如何做到这一点,但有一个简单(和愚蠢)的解决方法。

using System.Diagnostics;

namespace Test
{
  internal static class Program
  {
    private static void Main()
    {
      var dog = new Dog();
      SaveAnimal(dog);
    }

    private static void SaveAnimal(Animal animal)
    {
      var isAnimal = animal.GetType().UnderlyingSystemType == typeof(Animal);
      Debug.Assert(isAnimal, "You can not save dogs!");
    }

    private static void SaveAnimal(ICanNotSave animal)
    {
      Debug.Fail("Can not save");
    }
  }

  internal class Animal
  {
    public int Legs
    {
      get; set;
    }
  }

  internal interface ICanNotSave
  {
  }

  internal sealed class Dog : Animal, ICanNotSave
  {
    public int TailLength
    {
      get; set;
    }
  }
}

当你有两个SaveAnimal方法时,其中一个用于Animal和另一个用于接口,这是在所有无法保存的后代上实现的,编译器会报告一个CS0121错误。

以下方法或属性之间的调用不明确:'Program.SaveAnimal(Animal)'和'Program.SaveAnimal(ICanNotSave)'

请记住,当你像这样使用时,仍然可以使用SaveAnimal方法:SaveAnimal((Animal)dog)

© www.soinside.com 2019 - 2024. All rights reserved.