I'd like to ensure that two interfaces are never found on the same class at compile-time, similar to how AttributeUsage checks custom Attributes at compile-time.
e.g.:
[InterfaceUsage(MutuallyExclusive = typeof(B))]
interface A {
//...
}
interface B {
//...
}
class C : A, B { //sho开发者_运维技巧uld throw an error on compile time
//...
}
I can obviously do this at runtime with reflection, but I'm interested in a compile-time solution.
I'd imagine that one probably doesn't exist out of the box - but is there a way to create a custom attribute that is run at compile-time, much like AttributeUsage is?
A different approach could be to change them to Abstract classes.
How about giving them two methods with identical signatures, but incompatible return types?
A "workaround" would be to just add /// to both interfaces warning that they shouldn't be used together, because of such and such reason. It's addressing a pitfall with a caution sign, but it's better than nothing.
I ran into a similar case: Local and Global conditions. One can only depend on a fixed portion of the array around a position, while the other cannot depend on position at all. Put both together and you have a useless condition.
This may not be 100% of the way there, but you can obtain some errors at compile-time for interfaces that you want to be mutually-exclusive. Assuming we want to disallow people from implementing both of the interfaces cat
and dog
on the same class:
interface cat_or_dog
{
bool IsCute { get; }
}
interface cat_or_dog<A> : cat_or_dog where A : cat_or_dog
{
A Self { get; }
}
interface cat : cat_or_dog<cat>
{
Guid Meow { get; }
};
interface dog : cat_or_dog<dog>
{
Single Woof { get; }
};
With this setup, you will get actually get a fatal compile error if a class tries to implement both of the mutually exclusive interfaces publically, as shown in demo3
and demo4
. It's caused by a Type
collision on the return-value of a member in the shared base class cat_or_dog<A>
:
class demo1 : cat // ok
{
public bool IsCute => true;
public cat Self => this;
public Guid Meow => Guid.NewGuid();
};
class demo2 : dog // ok
{
public bool IsCute => true;
public dog Self => this;
public Single Woof => 10.0f;
};
class demo3 : cat, dog // CS0535: "doesn't implement 'dog.Woof' and 'cat_or_dog<dog>.Self'
{
public bool IsCute => default;
public cat Self => this;
public Guid Meow => Guid.NewGuid();
};
class demo4 : dog, cat // CS0535: "doesn't implement 'cat.Meow' and 'cat_or_dog<cat>.Self'
{
public bool IsCute => default;
public dog Self => this;
public Single Woof => 10.0f;
};
(The Meow
and Woof
errors are caused when the automatic interface implementation feature of the IDE not completing the refactoring after the more serious error, for some reason)
As you surely know however, this is a variation of the curiously recurring template pattern, so it is still possible to "incorrectly" implement both interfaces by using C# explicit interface implementation:
class demo5 : dog, cat // ok
{
public bool IsCute => true;
cat cat_or_dog<cat>.Self => this;
Guid cat.Meow => throw null;
dog cat_or_dog<dog>.Self => this;
float dog.Woof => 10.0f;
};
精彩评论