Espo
Espo

Reputation: 41939

Is it possible to restrict the type of variable passed to a .NET method to not be a derived class?

Can i constrain the type that is passed to my method in a way that catches this type of error when compiling instead of running?

My current code looks like this:

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

Upvotes: 1

Views: 97

Answers (3)

Mong Zhu
Mong Zhu

Reputation: 23732

Yes, but only with a workaround using generics and interfaces.

What you would need to do is to declare 3 interfaces

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

Now you need to give your Animal class a generic parameter and constraint it to be of type ISaveAbleBase.

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

This way you can now specify in the derived classes wether they can or cannot be saved:

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

Then you could make your method generic and constrain the type only to aminals that can be saved

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

Now the result looks the following way:

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

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

Disclaimer: this construct would also allow you to have a general Animal that cannot be saved:

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

PS. This answer is inspired by this post

Upvotes: 2

Julo
Julo

Reputation: 1120

There is no standard way how to do this, but there is a simple (and stupid) workaround.

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

When you have two SaveAnimal method, where one of for Animal and other is for a interface, that is implemented on all descendants that can not be saved, the compiler report a CS0121 error.

The call is ambiguous between the following methods or properties: 'Program.SaveAnimal(Animal)' and 'Program.SaveAnimal(ICanNotSave)'

Remember it is still possible to use the SaveAnimal method when you use it like this: SaveAnimal((Animal)dog)

Upvotes: 1

usr
usr

Reputation: 171206

No, there is no way in the language to statically catch this as a usage error.

You can assert this at runtime as you are doing it already. It's against the spirit of inheritance, though, because a fundamental assumption is that derived types must substitute for the base type (the Liskov substitution principle).

Maybe you can make the animal save itself by giving Animal a new method abstract void Save(). Then, each animal decides what to do. Dog can then throw a NotSupportedException.

Upvotes: 6

Related Questions