Osy#betaRun it on Osyrin: osyrin.com

Reference / Class

App.Implementations<T>()

List<T> App.Implementations<T>() // T is an interface

One new instance of every class in the app's composition — its own and every package's — that implements the interface T. How a kit finds every implementation of its contract without the app having to list them: an implementation nobody wired is still found.

preview3 examples compiled by CIclassinterfacecontractkit

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 place

Description#

  • 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#

Related

Interfaces — a contract several types can satisfy

An `interface` declares what a type must do without saying how — methods and property contracts, and no state. A member…

Finding a kit, and learning one from its tests

Lists the kits (packages) this project can build on — each one's contracts, the kits that implement them, and what this…

[Slot] and contributions — a place on a page that other kits, and the app, add to

A slot is a place on a page others add to. A kit marks one (`[Slot] public abstract component QuotePanel(Quote…