Option to demand implementations of interfaces/abstract classes only within the assembly that created them. #8128
Replies: 2 comments 11 replies
-
If you declare an |
Beta Was this translation helpful? Give feedback.
-
The best you can get (out of the box) would be a custom analyzer that raises errors when library consumers implement your interface. However, there is another, more interesting way, that can be used: IL rewriting. Avalonia, for example, have an attribute [NotClientImplementable] they tag to interfaces. Then, during build, they inject in a method with an "unspeakable" name, If you try to implement any of those interfaces, you get a compiler error about the method you "forgot" to implement: public class MyCustomPen : IPen
{
public IBrush? Brush { get; }
public IDashStyle? DashStyle { get; }
public PenLineCap LineCap { get; }
public PenLineJoin LineJoin { get; }
public double MiterLimit { get; }
public double Thickness { get; }
} Produces:
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Let us assume the following context: I have an interface or abstract class that specifies a number of methods and properties, which then will need an entity that implements these things in full. For the sake of security, users of the DLL of this library are not allowed to create their own custom implementations, but rather to use one of the existing implementations made available to them by the library itself.
I don't know what new keyword would fit, so for now let's call it 'local' (but I think there are better terms for ''only implemented locally''. localseal? localimpl?)
This means that the application (or other library) using this assembly is allowed to know about the interface and abstract class and is allowed to use them and call the methods and properties as specified by the contract, but aren't allowed to override them.
This prevents a junior programmer or some third party from abusing the API of the library as it was intended and cause unwanted behavior. Or maybe the code belongs to a firm who wants the behaviors of IFoo and its implementation Foo to be contained in a neat shared library, and not end up with a hundred implementations of IFoo floating around the code of their different products. I've seen this happen at previous employers where IFoo was implemented again and again, although this wasn't intended; it was intended that implementation Foo be used, although this information was lost in time.
To prevent the worst case scenario, this prevents one from creating a library which doesn't do what the original interface's contract demands at all but rather does something else for nefarious means. (e.g. the interface is about calling a Web API, but the implementation also turns your computer in a bitcoin miner, and since mr. programmer is in a hurry he just grabs the first nuget package he sees of an implementation).
It could also cause bugs where a third party implements the interface, and doesn't do exactly what the original programmers of the interface intended, so when the interface is called from within the original assembly, it could cause bugs.
Example:
In this example, BarWithValue() shouldn't return null since it would disrupt the behavior of the original assembly. So let's assume a third party does the following:
In this case, a third party misused the library, and we only want them to enact behaviors that we allow as specified by the interface contract.
For interfaces, implementing this is simple:
Examples:
this would be allowed, since we're using the implementations as specified in the API of the library we're using. FooImplementation was created in the original library as well. The using assembly knows what IFooInterface is, it's not hidden and we are allowed to use its properties and methods as-is. The only thing we're not allowed to do is override them. The following would cause an error:
If the entire interface or abstract class is marked local this is all pretty simple; use them as much as you like since it's simply a contract, but you're not going to make your own implementation.
If you want to mark specific properties or methods as local, however, this can be a bit more specific: for example:
If we were to override the Foo class outside the assembly where it was established, this would mean we cannot override an existing implementation of Foo: we can override Bar, but not FooBarOne
That would make the following illegal if FooImpl isn't part of the assembly as Foo:
This means that, if we want to expose abstract class FooImpl to the outside world, the methods marked local from the original would need to have been implemented. That means the following (where FooImpl is from the same assembly as Foo) is legal behavior:
Note: the overridden methods marked local must be marked as either local again or as sealed, for obvious reasons.
public sealed local override FooBarImplementation() { .. }
is kind of redundant.Beta Was this translation helpful? Give feedback.
All reactions