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

时间:2018-09-05 11:35:27

标签: c# .net

我可以限制传递给我的方法的类型,以便在编译而不是运行时捕获这种类型的错误吗?

我当前的代码如下:

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; }
}

3 个答案:

答案 0 :(得分:6)

否,该语言无法静态地将其视为使用错误。

您可以在运行时声明它,因为您已经在执行它了。但是,这与继承的精神背道而驰,因为一个基本的假设是派生类型必须替换基类型(Liskov替换原理)。

也许您可以通过给Animal一种新方法abstract void Save()使动物自救。然后,每只动物决定做什么。 Dog然后可以抛出NotSupportedException

答案 1 :(得分: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

答案 2 :(得分: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错误。 / p>

  

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

请记住,当您使用SaveAnimal方法时,仍然可以使用SaveAnimal((Animal)dog)