ImpromptuInterface: a static interface over any C# object
Static interface to dynamic implementation (duck casting). Uses the DLR combined with Reflect.Emit.
At a glance
- What is it?
- ImpromptuInterface lets you treat an anonymous type or an ExpandoObject through a compile-time interface by emitting and caching dynamic binding code with the DLR and Reflect.Emit. It solves the adapter problem in one call, at the cost of a runtime dependency on the DLR and a project that has been split across three repositories.
- Who is it for?
- ImpromptuInterface suits .NET developers who need an interface over objects they do not control, such as deserialised payloads, expando shapes or legacy components, and who want the compile-time contract without writing an adapter class by hand. It is the wrong choice for a public API surface, where the emitted proxy hides the real type from consumers and where a plain hand-written adapter is cheaper to maintain.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 30 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A proxy that presents any object as your interface
ImpromptuInterface is a framework for .NET 4.0 and netstandard2.0 that lets you wrap any object, static or dynamic, with a static interface even though the object never inherited from it. The description calls the technique duck casting, and the mechanism is a proxy. Instead of an adapter class you write by hand, the library generates a type at run time that implements your interface and forwards each member to the underlying object. That matters most when the interface is the stable contract and the implementation is not yours. A deserialised object, an ExpandoObject with a shape decided at run time, a legacy component, a mock, these are all cases where writing a wrapper is tedious and where the wrapper tends to drift. The framework is available on NuGet as ImpromptuInterface, and the repository also carries a Benchmarks directory and a published test results site, so the project treats the overhead of the proxy as something it wants measured.
The DLR and Reflect.Emit do the emitting and caching
The mechanism is spelled out in the project description: the library uses the DLR combined with Reflect.Emit. The Dynamic Language Runtime supplies the expression trees and call site machinery that let a proxy forward a statically typed call to an object the compiler knows nothing about, and Reflect.Emit produces the actual IL for the generated proxy type. The word cached in the description matters for performance. Emitting a type is expensive relative to a normal method call, so the library keeps the generated binding code around and reuses it rather than emitting fresh code for every instance. That is the difference between a pattern that is fine for a handful of calls and one that is fine in a hot path. The repository layout backs the claim of a measured approach: a Benchmarks directory sits alongside the main project and the Tests directory, and the README links a published coverage report for the latest master build.
Declaring the interface and acting on an expando
Usage starts with an ordinary interface declaration. Nothing about it is special, which is the point: the interface is a static contract you can compile against, and the implementation arrives later:
using ImpromptuInterface;
using Dynamitey;
public interface IMyInterface{
string Prop1 { get; }
long Prop2 { get; }
Guid Prop3 { get; }
bool Meth1(int x);
}The object can then be an ExpandoObject, where the properties are added at run time, or an anonymous type, and either can be treated as the interface. The Expando route sets properties and a method on a dynamic expando, then converts it:
//Dynamic Expando object
dynamic expando = new ExpandoObject();
expando.Prop1 ="Test";
expando.Prop2 = 42L;
expando.Prop3 = Guid.NewGuid();
expando.Meth1 = Return<bool>.Arguments<int>(it => it > 5);
IMyInterface myInterface = Impromptu.ActLike(expando);The Return<bool>.Arguments<int> helper comes from Dynamitey, which is a separate library the README says ImpromptuInterface depends on. With an anonymous type the conversion is an extension method, anon.ActLike<IMyInterface>(), and the anonymous object is built with the same Return helper for the method member. From that point myInterface is an ordinary typed reference, and a missed member surfaces when the call is bound, not when the code is compiled.
The project has been split into three pieces
The repository you look at is not the whole library any more, and that matters if you are picking a package. The README says some features of ImpromptuInterface have been moved into another library called Dynamitey, and that ImpromptuInterface depends on Dynamitey, which explains the using directive in the example above and the dependency in the import list. The F# binding, ImpromptuInterface.FSharp, has been spun off into FSharp.Interop.Dynamic, and that project also depends on Dynamitey. A third piece, ImpromptuInterface.MVVM, now lives in its own repository and, in the README's own wording, only receives maintenance updates. The practical consequence is dependency resolution rather than confusion: an F# user ends up in a different repository for the same idea, an MVVM user in another, and everyone pulls Dynamitey. Anyone upgrading from an older version should expect the packages to have moved and the transitive dependencies to have changed.
Target frameworks, build legs and coverage
The build story is broad for a library of this size. One workflow covers net40, netstandard2.0 and net10.0 on Windows, macOS and Linux, plus a browser-wasm leg. The README publishes the test results and coverage for the latest master build at ekonbenefits.github.io, and notes that every run carries its own per-platform table in the run summary. The badges in the README report 217 tests passed, 91 percent line coverage and 82 percent branch coverage. At the repository root there is a global.json, a Directory.Build.props, an ImpromptuInterface.sln and a NuGet.config, which is the conventional set of files for a multi-target .NET library that has to keep several runtime identifiers building at once. The presence of a browser-wasm leg is the detail that stands out, since it implies the emitted proxy has to work in a runtime where some of the reflection surface people expect is not there. The README does not say how that constraint shaped the implementation, so anyone targeting the web should check the proxy path specifically.
What the proxy costs you at run time
The honest limitation is that a static interface over a dynamic object is not free, and the failure modes are specific. The compiler cannot check that the underlying object has the members, because at compile time it has no knowledge of the object at all, so a missing member becomes a run-time failure rather than a build error. The proxy also sits between your call and the real implementation, which adds indirection to every member access and to every interface cast. A second constraint is that the interface describes a shape, not a contract: the object may have a property with the right name and an incompatible type or behaviour, and nothing in the mechanism catches that. Where the objects are yours and stable, a hand-written adapter class is dull but exact, and where the shape is fixed at compile time, the C# dynamic keyword, which the second example shows, gives you late binding without a generated type at all. The difference in approach is that ImpromptuInterface buys you the static type at the cost of a generated proxy and a run-time resolution step, while dynamic gives you late binding at the cost of no type safety, and an adapter gives you both at the cost of code you have to maintain.
Releases, licence and upgrade path
The version history shows a project that keeps shipping. The most recent tag is v8.1.0 from 2026-09-23, the previous one is v8.0.6 from 2025-01-08, and before that v8.0.4 from 2023-11-08. The last push to the default branch was on 2026-09-23, the same day as the v8.1.0 tag, so the branch and the release are in step at the moment of writing. The licence is Apache-2.0, with the text kept in a License.txt file at the repository root, and the code is also distributed through NuGet under the ImpromptuInterface package. The upgrade path for an existing user is a package version bump, but with the splits described earlier it is worth checking which of the three repositories your code actually consumes, because an update to ImpromptuInterface alone will not update the MVVM or F# pieces, and Dynamitey moves underneath you either way. The 8.x version line also signals a fairly settled API, which is the opposite of the beta situation some dynamic libraries live in.
Editorial conclusion
ImpromptuInterface suits .NET developers who need an interface over objects they do not control, such as deserialised payloads, expando shapes or legacy components, and who want the compile-time contract without writing an adapter class by hand. It is the wrong choice for a public API surface, where the emitted proxy hides the real type from consumers and where a plain hand-written adapter is cheaper to maintain. Before adopting it, check that the members on your interface exist on the target object at run time, since the binding is resolved when the proxy is used rather than when the code is compiled.
Frequently asked questions
What does ActLike do in ImpromptuInterface?
It wraps an object so it can be used through an interface it does not implement. The library emits and caches dynamic binding code in a proxy that forwards each member to the underlying object, which works for anonymous types, ExpandoObject instances and any other object.
Which .NET versions does ImpromptuInterface support?
The README says one workflow covers net40, netstandard2.0 and net10.0 on Windows, macOS and Linux, plus a browser-wasm leg. The package is published on NuGet as ImpromptuInterface.
What is the relationship between ImpromptuInterface and Dynamitey?
ImpromptuInterface depends on Dynamitey, and some features were moved out into it. The Return helper used to build a method for an expando comes from Dynamitey, which is why the examples import both namespaces.
Where did the F# and MVVM parts of ImpromptuInterface go?
The F# binding was spun off into FSharp.Interop.Dynamic, and ImpromptuInterface.MVVM now lives in its own repository and only receives maintenance updates.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ekonbenefits-impromptu-interface)Was this useful?