Summary#
App.Implementations<IPersonalDataHolder>() answers a List<IPersonalDataHolder> holding one new instance of every
concrete class, anywhere in the app, that implements the interface — the kit's own, another kit's, the app's. A kit
uses it where a forgotten wiring would be a silent gap: the Privacy kit asks it for every holder of personal data, so
an erasure reaches a kit the app never thought to list.
Signature#
List<T> App.Implementations<T>() // T must be an interface
[Order(20)] class MySource : ISource { … } // its place among the others, lower first
class WorkSource<T> : ISource where T : WorkItem { … } // written once, closed for every kind of WorkItem
use Acme.Reports@0 { without WorkSource<PurchaseRequest>; } // app.osy: leave one out
[Replaces(TicketSource)] class MyTicketSource : ISource { … } // the app's own, in its placeDescription#
- Answered over the whole app — a kit's function is compiled in the package's own pass and has never seen the app's classes, and still finds them: the app's compile writes the answer (the contribution index), and the kit's call reads it.
- Each instance is built by a real
new— its field initializers applied, an expression one included (public string Kind = typeof(T).Name;) and its constructor that takes no arguments run. A class whose every constructor takes arguments is not discovered — C# cannot build one either; derive a class that passes them (public CustomerFields() : base(v => v.Customer, …) { }), and that one is found. - Abstract classes and interfaces are left out; a subclass of an implementing class is included.
In what order do they come?#
The composition's one order: [Order(n)] first, lower first, 100 when none is said; then the package order — a kit
before the kits built on it, the app last; then the full name. The same order slots draw their contributions in
([Slot] and contributions — a place on a page that other kits, and the app, add to).
How is one class written for every record type?#
A class with one type parameter bounded by an ENTITY that implements a contract — class WorkSource<T> : ISource where T : WorkItem — is closed by the compiler for every concrete subtype of the bound in the app (WorkSource_Ticket,
WorkSource_Bug): T is that type in each copy, so T.Count(), T.Where(…) and typeof(T).Name are ordinary code.
The open class itself is a template and is never built.
What can the app change?#
use Kit@0 { without SomeSource; } leaves a package's implementation out (without WorkSource<Bug>; one closed copy,
without WorkSource; all of them) — a without that names nothing is refused with the package's list.
[Replaces(SomeSource)] on the app's own class implementing the same contract answers it in that one's place.
Examples#
public interface IGreeter { string Greet(); }
class English : IGreeter { public string Greet() => "hello"; }
class Swedish : IGreeter { public string Greet() => "hej"; }
string AllGreetings() {
var all = "";
foreach (var g in App.Implementations<IGreeter>()) { all = all + g.Greet() + " "; }
return all.Trim();
}[Test]
void Every_implementation_is_found() {
Assert.Equal(2, App.Implementations<IGreeter>().Count);
Assert.Contains("hej", AllGreetings());
}public interface ISource { string Describe(); }
entity Work {
[MaxLength(100)] string Title = "";
security { allow read, create when IsAnonymous || IsAuthenticated; }
}
entity Ticket : Work { }
entity Bug : Work { }
/// Closed for Ticket and for Bug: `T` is each.
class WorkSource<T> : ISource where T : Work {
public string Kind = typeof(T).Name;
public string Describe() { return Kind + "=" + T.Count(); }
}
[Order(5)]
class Overall : ISource { public string Describe() { return "all=" + Work.Count(); } }
/// "all=…;Bug=…;Ticket=…;"
string Sources() {
var said = "";
foreach (var s in App.Implementations<ISource>()) { said = said + s.Describe() + ";"; }
return said;
}See also#
- [Slot] and contributions — a place on a page that other kits, and the app, add to — the same order,
withoutand[Replaces]for what a kit's page draws - Interfaces — a contract several types can satisfy — declaring a contract, with defaulted members
- Finding a kit, and learning one from its tests —
osy kits <name>lists a contract's implementations from the command line