Reputation: 41939
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
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
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
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