How to extend class with an extra property - c#

Suppose I've got a class named Foo.
I cannot change the Foo class but I wan't to extend it with a property named Bar of type string.
Also I've got a lot more classes like Foo so I'm interested in a 'generic' solution.
I'm looking into ExpandoObject, dynamic and it gives me the result I'm asking for but I was wondering it it could be done without using dynamic...
static void Main(string[] args)
{
var foo = new Foo() { Thing = "this" };
var fooplus = Merge(foo, new { Bar = " and that" });
Console.Write(string.Concat(fooplus.Thing, fooplus.Bar));
Console.ReadKey();
}
public class Foo
{
public string Thing { get; set; }
}
public static dynamic Merge(object item1, object item2)
{
if (item1 == null || item2 == null)
return item1 ?? item2 ?? new ExpandoObject();
dynamic expando = new ExpandoObject();
var result = expando as IDictionary<string, object>;
foreach (System.Reflection.PropertyInfo fi in item1.GetType().GetProperties())
{
result[fi.Name] = fi.GetValue(item1, null);
}
foreach (System.Reflection.PropertyInfo fi in item2.GetType().GetProperties())
{
result[fi.Name] = fi.GetValue(item2, null);
}
return result;
}

Your problem can relatively easily be solved by using Reflection.Emit and run-time code generation.
Suppose now you have the following class that you would like to extend.
public class Person
{
public int Age { get; set; }
}
This class represents a person, and contains a property named Age to represent the person's age.
In your case, you would also like to add a Name property of type string to represent the person's name.
The simplest and most streamlined solution would then be to define the following interface.
public interface IPerson
{
string Name { get; set; }
int Age { get; set; }
}
This interface, which will be used to extend your class, should contain all the old properties your current class contains, and the new ones you would like to add. The reason for this will become clear in a moment.
You can now use the following class definition to actually extend your class by creating a new type at runtime which will also make it derive from the above mentioned interface.
class DynamicExtension<T>
{
public K ExtendWith<K>()
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Assembly"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Module");
var type = module.DefineType("Class", TypeAttributes.Public, typeof(T));
type.AddInterfaceImplementation(typeof(K));
foreach (var v in typeof(K).GetProperties())
{
var field = type.DefineField("_" + v.Name.ToLower(), v.PropertyType, FieldAttributes.Private);
var property = type.DefineProperty(v.Name, PropertyAttributes.None, v.PropertyType, new Type[0]);
var getter = type.DefineMethod("get_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, v.PropertyType, new Type[0]);
var setter = type.DefineMethod("set_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, null, new Type[] { v.PropertyType });
var getGenerator = getter.GetILGenerator();
var setGenerator = setter.GetILGenerator();
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Ldfld, field);
getGenerator.Emit(OpCodes.Ret);
setGenerator.Emit(OpCodes.Ldarg_0);
setGenerator.Emit(OpCodes.Ldarg_1);
setGenerator.Emit(OpCodes.Stfld, field);
setGenerator.Emit(OpCodes.Ret);
property.SetGetMethod(getter);
property.SetSetMethod(setter);
type.DefineMethodOverride(getter, v.GetGetMethod());
type.DefineMethodOverride(setter, v.GetSetMethod());
}
return (K)Activator.CreateInstance(type.CreateType());
}
}
To actually use this class, simply execute the following lines of code.
class Program
{
static void Main(string[] args)
{
var extended = new DynamicExtension<Person>().ExtendWith<IPerson>();
extended.Age = 25;
extended.Name = "Billy";
Console.WriteLine(extended.Name + " is " + extended.Age);
Console.Read();
}
}
You can now see that the reason we used an interface to extend our newly created class is so that we can have a type-safe way of accessing its properties. If we simply returned an object type, we would be forced to access its properties by Reflection.
EDIT
The following modified version is now able to instantiate complex types located inside the interface, and implement the other simple ones.
The definition of the Person class stays the same, while the IPerson interface now becomes the following.
public interface IPerson
{
string Name { get; set; }
Person Person { get; set; }
}
The DynamicExtension class definition now changes to the following.
class DynamicExtension<T>
{
public T Extend()
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Assembly"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Module");
var type = module.DefineType("Class", TypeAttributes.Public);
type.AddInterfaceImplementation(typeof(T));
foreach (var v in typeof(T).GetProperties())
{
var field = type.DefineField("_" + v.Name.ToLower(), v.PropertyType, FieldAttributes.Private);
var property = type.DefineProperty(v.Name, PropertyAttributes.None, v.PropertyType, new Type[0]);
var getter = type.DefineMethod("get_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, v.PropertyType, new Type[0]);
var setter = type.DefineMethod("set_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, null, new Type[] { v.PropertyType });
var getGenerator = getter.GetILGenerator();
var setGenerator = setter.GetILGenerator();
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Ldfld, field);
getGenerator.Emit(OpCodes.Ret);
setGenerator.Emit(OpCodes.Ldarg_0);
setGenerator.Emit(OpCodes.Ldarg_1);
setGenerator.Emit(OpCodes.Stfld, field);
setGenerator.Emit(OpCodes.Ret);
property.SetGetMethod(getter);
property.SetSetMethod(setter);
type.DefineMethodOverride(getter, v.GetGetMethod());
type.DefineMethodOverride(setter, v.GetSetMethod());
}
var instance = (T)Activator.CreateInstance(type.CreateType());
foreach (var v in typeof(T).GetProperties().Where(x => x.PropertyType.GetConstructor(new Type[0]) != null))
{
instance.GetType()
.GetProperty(v.Name)
.SetValue(instance, Activator.CreateInstance(v.PropertyType), null);
}
return instance;
}
}
We can now simply execute the following lines of code to get all the appropriate values.
class Program
{
static void Main(string[] args)
{
var extended = new DynamicExtension<IPerson>().Extend();
extended.Person.Age = 25;
extended.Name = "Billy";
Console.WriteLine(extended.Name + " is " + extended.Person.Age);
Console.Read();
}
}

as my comments were getting very verbose, I thought I'd add a new answer. this answer is completely Mario's work and thinking, only has my minor addition to exemplify what I'm trying to put across.
There are a few minor changes to mario's example that would make this work very well, namely, just changing the fact that the existing properties are added as the class object, rather than duplicating the entire class. Anyway, here's how this looks (only amended sections added, all else remains as per mario's answer):
public class Person
{
public int Age { get; set; }
public string FaveQuotation { get; set; }
}
for the IPerson interface, we add the actual Person class, rather than copying the properties:
public interface IPerson
{
// extended property(s)
string Name { get; set; }
// base class to extend - tho we should try to overcome using this
Person Person { get; set; }
}
This translates to an updated usage of:
static void Main(string[] args)
{
var extended = new DynamicExtension<Person>().ExtendWith<IPerson>();
var pocoPerson = new Person
{
Age = 25,
FaveQuotation = "2B or not 2B, that is the pencil"
};
// the end game would be to be able to say:
// extended.Age = 25; extended.FaveQuotation = "etc";
// rather than using the Person object along the lines below
extended.Person = pocoPerson;
extended.Name = "Billy";
Console.WriteLine(extended.Name + " is " + extended.Person.Age
+ " loves to say: '" + extended.Person.FaveQuotation + "'");
Console.ReadKey();
}
Hope this helps the original OP, I know it made me think, tho the jury is still out as to whether the Person class should be flattened to the same level in the method as the new properties!! So in effect, using the line new DynamicExtension<Person>().ExtendWith<IPerson>(); SHOULD return a fully extended new object -intellisence included. Tough call - hmmm...

Without having access to the class definition, the best you could do is create a class which is derived from the target class. Unless the original is Sealed.

I know this is coming late. A nuget package that abstracts all the complexity required to extend a type at runtime has been created. It is as simple as:
var className = "ClassA";
var baseType = typeof(List<string>);
var typeExtender = new TypeExtender(className, baseType);
typeExtender.AddProperty("IsAdded", typeof(bool));
typeExtender.AddProperty<double>("Length");
var returnedClass = typeExtender.FetchType();
var obj = Activator.CreateInstance(returnedClass);
You can find more usage instructions on the repo TypeExtender. Nuget package is at nuget

Related

How can I Remove Item from Queue or List with Unknown Type?

I have the following with a private nested struct
public class A {
private Queue<B> blah = new Queue<B>();
private struct B { some fields... }
}
and I need to modify blah at runtime. I can't touch that code; it belongs in another dll.
So far I tried
A test = new A();
FieldInfo f = typeof(A).GetField("blah",BindingFlags.Instance | BindingFlags.NonPublic);
object k = f.GetValue(test);
List<object> list = ((ICollection)k).Cast<object>().ToList();
list.RemoveAt(0);
var nextQ = Activator.CreateInstance(f.GetUnderlyingType());
//How do I copy the modified list into nextQ??
f.SetValue(test, nextQ);
The problem is, nextQ is of type object. So how can I copy the modified list into it? Activator has an overload for calling Queue's constructor, but I can't use that since I can't cast list to IEnumerable<B>, and I just get an error about the constructor is not found.
If you just need to dequeue an item, just call Dequeue() method on the queue.
assuming you have the following in the other assembly:
internal class B
{
public string? Prop { get; set;}
}
public class A
{
private Queue<B> blah;
public A()
{
blah = new Queue<B>();
blah.Enqueue(new B { Prop = "1" });
blah.Enqueue(new B { Prop = "2" });
blah.Enqueue(new B { Prop = "3" });
}
}
You can write the following code to dequeue one item:
var a = new A();
var queueField = typeof(A).GetField("blah", BindingFlags.Instance | BindingFlags.NonPublic);
var queueValue = queueField.GetValue(a);
var queueType = queueValue.GetType();
var dequeueMethod = queueType.GetMethod("Dequeue");
var countProperty = queueType.GetProperty("Count");
Console.WriteLine(countProperty.GetValue(queueValue).ToString());
object dequeuedObject = dequeueMethod.Invoke(queueValue, null);
Console.WriteLine(countProperty.GetValue(queueValue).ToString());

Reflection and dynamic in-memory assemblies

Let us say I create a type dynamically using CSharpCodeProvider and choose NOT to persist the results. The assembly that is generated is existing only in memory.
Let us say I Create two types in two different in-memory assemblies:
Assembly1:
public class DynamicTypeA { }
Assembly2:
public class DynamicTypeB
{
public DynamicTypeA MyProperty { get; set; }
}
As you can see the second type has a property of the first type.
Cool. Now I want to explore DynamicTypeB using reflection:
foreach (PropertyInfo pi in typeof(DynamicTypeB).GetProperties())
{
Console.WriteLine(pi.PropertyType.Name);
}
It turns out that PropertyInfo.PropertyType fails when the assembly is not located on disk !!!
This is true for MemberInfo and for all other type investigation constructs.
As we all know lots of .Net APIs is using type investigation on the backend and they would fail when the investigated type happens to live in an in-memory assembly. For Example Expression.Bind takes a MemberInfo as the first parameter and is using it to validate that the type of the expression provided in the second parameter matches the type of the member. When this type happens to be in an in memory assembly Expression.Bind fails.
Can anyone think of a solution?
Creating types dynamically and writing them do disk pollutes the running environment and that is bad, yet without reflection these types are worthless.
Thanks
Manu
It turns out that PropertyInfo.PropertyType fails when the assembly is not located on disk
Are you sure? Take a look:
static void Main( string[] args )
{
string code = #"
namespace foo {
public class DynamicTypeA { }
public class DynamicTypeB {
public DynamicTypeA MyProperty { get; set; }
}
}
";
CSharpCodeProvider csp = new CSharpCodeProvider();
CompilerParameters p = new CompilerParameters();
p.GenerateInMemory = true;
var results = csp.CompileAssemblyFromSource( p, code );
foreach ( Type type in results.CompiledAssembly.GetTypes() )
{
Console.WriteLine( type.Name );
foreach ( PropertyInfo pi in type.GetProperties() )
{
Console.WriteLine( "\t{0}", pi.PropertyType.Name );
}
}
Console.ReadLine();
}
This uses your snippet and works like a charm.
Moving the loop to the inside of the dynamic code doesn't change much, it still works:
string code = #"
using System;
using System.Reflection;
namespace foo {
public class DynamicTypeA { }
public class DynamicTypeB {
public DynamicTypeA MyProperty { get; set; }
}
public class DynamicTypeC {
public void Foo() {
foreach ( PropertyInfo pi in typeof(DynamicTypeB).GetProperties() )
{
Console.WriteLine( pi.PropertyType.Name );
}
}
}
}
";
CSharpCodeProvider csp = new CSharpCodeProvider();
CompilerParameters p = new CompilerParameters();
p.GenerateInMemory = true;
var results = csp.CompileAssemblyFromSource( p, code );
var c = results.CompiledAssembly.CreateInstance( "foo.DynamicTypeC" );
var typeC = c.GetType();
typeC.InvokeMember( "Foo", BindingFlags.InvokeMethod |
BindingFlags.Public | BindingFlags.Instance, null, c, null );
If for some reason you have issues here, you are definitely doing something more complicated.
I found the problem:
I need to load the dynamically compiled assemblies into the current AppDomain, only then I will be able to retrieve any info by reflection.
I would like to thank Sam Alavi for explaining this to me.
Here is the code with the necessary fix:
public class HomeController : Controller
{
public Assembly AssemblyA { get; set; }
public Assembly AssemblyB { get; set; }
public ActionResult Index()
{
var provider = new CSharpCodeProvider();
var parametersA = new CompilerParameters();
parametersA.GenerateInMemory = true;
parametersA.OutputAssembly = "dynamicA.dll";
var code1 = #"namespace DynamicA { public class DynamicClassA { } }";
var result1 = provider.CompileAssemblyFromSource(parametersA, code1);
this.AssemblyA = result1.CompiledAssembly;
var parametersB = new CompilerParameters();
parametersA.GenerateInMemory = true;
parametersB.ReferencedAssemblies.Add("dynamicA.dll");
parametersB.OutputAssembly = "dynamicB.dll";
var code2 = #"using DynamicA; namespace DynamicB { public class DynamicB { public DynamicClassA MyProperty { get; set; } } }";
var results2 = provider.CompileAssemblyFromSource(parametersB, code2);
this.AssemblyB = results2.CompiledAssembly;
AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
{
if (e.Name.Contains("dynamicA"))
return this.AssemblyA;
if (e.Name.Contains("dynamicB"))
return this.AssemblyB;
return null;
};
AppDomain.CurrentDomain.Load(this.AssemblyA.FullName);
AppDomain.CurrentDomain.Load(this.AssemblyB.FullName);
var t = results2.CompiledAssembly.DefinedTypes.First();
var pi = t.GetProperty("MyProperty");
var res = pi.PropertyType.Name;
return View(res);
}
}

Dynamic property assigned to object

Is it possible to attach dynamic property to an object of user-defined class?
public class Room
{
public int NumberOfDoors { get; set; }
public int NumberOfWindows { get; set; }
}
then from other context:
Room room = new Room();
dynamic contact = new ExpandoObject();
contact.NumberOfWalls = 4;
and then somehow associate NumberOfWalls with room, as its property?
Update (Larger Picture):
as per #nawfal's suggestion
I have a cached List<Room> being iterated in a razor view (outside the themes folder), calling a
particular partial view (from the current theme) for each element. One of the theme needs an extra
property in Room. I only have access to modify code in that particular theme folder, the partial
views cshtml files (don't ask why).
So its basically:
(psuedocode)
Room.NoOfWalls = SomeHeavyLiftingProcess(Room.NoOfWindows, Room.NoOfDoors)
I am looking for a way o update the List<Room> rooms object with NoOfWalls in
HttpRuntime.Cache["rooms"] to avoid calling SomeHeavyLiftingProcess() with each request. The
goal is to inject a property in cached object. Unfortuntely HttpRuntime.Cache["rooms"] is object
type and doesn't allow me to do this:
HttpRuntime.Cache["rooms"][3]["NoOfWalls"] = SomeHeavyLiftingProcess(..)
So I am thinking, for the first request (when cache is empty or invalid):
Unpackig: Retrieve (List<Room>)HttpRuntime.Cache["room"], inject NoOfWalls in the current room object.
Repacking: Update List<Room> room with the new object and assign it back to HttpRuntime.Cache.
For the subsequent requests, the value of NoOfWalls will come from cached object #Model.NoOfWalls.
You cannot add properties not defined in a class to an existing instance, without using a dynamic object like ExpandoObject.
If you need to add members to an existing class, you can create a child class with a special constructor:
public class SpecialRoom : Room
{
public SpecialRoom() { }
public SpecialRoom(Room copy)
{
this.NumberOfDoors = copy.NumberOfDoors;
this.NumberOfWindows = copy.NumberOfWindows;
}
public int NumberOfJacuzzis { get; set; }
}
Usage:
var room = new Room();
room.NumberOfDoors = 3;
var specialRoom = new SpecialRoom(room)
{
NumberOfJacuzzis = 7
};
Or:
var listOfRooms = new List<Room>();
// ...
var listOfSpecialRooms = listOfRooms.Select(x => new SpecialRoom(x));
listOfSpecialRooms.ForEach(x => x.NumberOfJacuzzis = ComplexCalculation(x));
If you have an existing concrete object (like an instance of the Room class), you can convert it to a dynamic object with a method like this:
public static dynamic ConvertObjectToDynamic(object value)
{
if (value == null)
{
return null;
}
IDictionary<string, object> dynamicObject = new ExpandoObject();
var properties = value.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance);
foreach (var propertyInfo in properties)
{
if (propertyInfo.GetIndexParameters().Length == 0)
{
var propertyValue = propertyInfo.GetValue(value);
dynamicObject[propertyInfo.Name] = propertyValue;
}
}
return dynamicObject;
}
Usage:
var room = new Room();
room.NumberOfDoors = 3;
dynamic dynamicObject = ConvertToDynamic(room);
dynamicObject.WhateverYouWant = 7;
Now dynamicObject.NumberOfDoors will be 3, and dynamicObject.WhateverYouWant will be 7.

How to add an attribute to a property at runtime

//Get PropertyDescriptor object for the given property name
var propDesc = TypeDescriptor.GetProperties(typeof(T))[propName];
//Get FillAttributes methodinfo delegate
var methodInfo = propDesc.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic)
.FirstOrDefault(m => m.IsFamily || m.IsPublic && m.Name == "FillAttributes");
//Create Validation attribute
var attribute = new RequiredAttribute();
var attributes= new ValidationAttribute[]{attribute};
//Invoke FillAttribute method
methodInfo.Invoke(propDesc, new object[] { attributes });
Hi I am trying to add Validation attribute at runtime using the above code. However I am getting the below exception:
Collection was of a fixed size
Don't let someone tell you that you can't do it. You can run for president if you want :-)
For your convenience, this is a fully working example
public class SomeAttribute : Attribute
{
public SomeAttribute(string value)
{
this.Value = value;
}
public string Value { get; set; }
}
public class SomeClass
{
public string Value = "Test";
}
[TestMethod]
public void CanAddAttribute()
{
var type = typeof(SomeClass);
var aName = new System.Reflection.AssemblyName("SomeNamespace");
var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);
var mb = ab.DefineDynamicModule(aName.Name);
var tb = mb.DefineType(type.Name + "Proxy", System.Reflection.TypeAttributes.Public, type);
var attrCtorParams = new Type[] { typeof(string) };
var attrCtorInfo = typeof(SomeAttribute).GetConstructor(attrCtorParams);
var attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[] { "Some Value" });
tb.SetCustomAttribute(attrBuilder);
var newType = tb.CreateType();
var instance = (SomeClass)Activator.CreateInstance(newType);
Assert.AreEqual("Test", instance.Value);
var attr = (SomeAttribute)instance.GetType()
.GetCustomAttributes(typeof(SomeAttribute), false)
.SingleOrDefault();
Assert.IsNotNull(attr);
Assert.AreEqual(attr.Value, "Some Value");
}
Sorry, I'm very late to the party. This is for those who might come here later. The top answer is awesome. Recently, a library has been developed, that abstracts away all of that complexity, and gives you something as simple as this:
var attributeType = typeof(CustomAAttribute);
var attributeParams = new object[] { "Jon Snow" };
var typeExtender = new TypeExtender("ClassA");
typeExtender.AddProperty("IsAdded", typeof(bool), attributeType, attributeParams);
To work with. details of how to install and use the library can be found here
Disclaimer: I developed this library and I've been using it for a lot of projects and it works like magic
Use FastDeepCloner which I developed.
public class test{
public string Name{ get; set; }
}
var prop = DeepCloner.GetFastDeepClonerProperties(typeof(test)).First();
prop.Attributes.Add(new JsonIgnoreAttribute());
// now test and se if exist
prop = DeepCloner.GetFastDeepClonerProperties(typeof(test)).First();
bool containAttr = prop.ContainAttribute<JsonIgnoreAttribute>()
// or
JsonIgnoreAttribute myAttr = prop.GetCustomAttribute<JsonIgnoreAttribute>();
It is not working because the FillAttributes method expects a parameter of the IList type and you are passing an array. Below is the implementation of MemberDescriptor.FillAttributes:
protected virtual void FillAttributes(IList attributeList) {
if (originalAttributes != null) {
foreach (Attribute attr in originalAttributes) {
attributeList.Add(attr);
}
}
}
As you can see, FillAttributes just fills the attributeList parameter with all attributes of your property. And to make your code work, change the var attributes= new ValidationAttribute[]{attribute}; line with:
var attributes = new ArrayList { attribute };
This code has nothing with adding attributes to property of type at runtime. This is "adding attribute to the PropertyDescriptor" extracted from type and has no sense unless you are trying to build a type at runtime which is based on an already existing type.
It is not possible to add Attributes in run-time. Attributes are static and cannot be added or removed.
Similar questions:
Can attributes be added dynamically in C#?
Remove C# attribute of a property dynamically

Can anonymous class implement interface?

Is it possible to have an anonymous type implement an interface?
I've got a piece of code that I would like to work, but don't know how to do this.
I've had a couple of answers that either say no, or create a class that implements the interface construct new instances of that. This isn't really ideal, but I'm wondering if there is a mechanism to create a thin dynamic class on top of an interface which would make this simple.
public interface DummyInterface
{
string A { get; }
string B { get; }
}
public class DummySource
{
public string A { get; set; }
public string C { get; set; }
public string D { get; set; }
}
public class Test
{
public void WillThisWork()
{
var source = new DummySource[0];
var values = from value in source
select new
{
A = value.A,
B = value.C + "_" + value.D
};
DoSomethingWithDummyInterface(values);
}
public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
{
foreach (var value in values)
{
Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
}
}
}
I've found an article Dynamic interface wrapping that describes one approach. Is this the best way of doing this?
No, anonymous types cannot implement an interface. From the C# programming guide:
Anonymous types are class types that consist of one or more public read-only properties. No other kinds of class members such as methods or events are allowed. An anonymous type cannot be cast to any interface or type except for object.
While the answers in the thread are all true enough, I cannot resist the urge to tell you that it in fact is possible to have an anonymous class implement an interface, even though it takes a bit of creative cheating to get there.
Back in 2008 I was writing a custom LINQ provider for my then employer, and at one point I needed to be able to tell "my" anonymous classes from other anonymous ones, which meant having them implement an interface that I could use to type check them. The way we solved it was by using aspects (we used PostSharp), to add the interface implementation directly in the IL. So, in fact, letting anonymous classes implement interfaces is doable, you just need to bend the rules slightly to get there.
Casting anonymous types to interfaces has been something I've wanted for a while but unfortunately the current implementation forces you to have an implementation of that interface.
The best solution around it is having some type of dynamic proxy that creates the implementation for you. Using the excellent LinFu project you can replace
select new
{
A = value.A,
B = value.C + "_" + value.D
};
with
select new DynamicObject(new
{
A = value.A,
B = value.C + "_" + value.D
}).CreateDuck<DummyInterface>();
Anonymous types can implement interfaces via a dynamic proxy.
I wrote an extension method on GitHub and a blog post http://wblo.gs/feE to support this scenario.
The method can be used like this:
class Program
{
static void Main(string[] args)
{
var developer = new { Name = "Jason Bowers" };
PrintDeveloperName(developer.DuckCast<IDeveloper>());
Console.ReadKey();
}
private static void PrintDeveloperName(IDeveloper developer)
{
Console.WriteLine(developer.Name);
}
}
public interface IDeveloper
{
string Name { get; }
}
No; an anonymous type can't be made to do anything except have a few properties. You will need to create your own type. I didn't read the linked article in depth, but it looks like it uses Reflection.Emit to create new types on the fly; but if you limit discussion to things within C# itself you can't do what you want.
The best solution is just not to use anonymous classes.
public class Test
{
class DummyInterfaceImplementor : IDummyInterface
{
public string A { get; set; }
public string B { get; set; }
}
public void WillThisWork()
{
var source = new DummySource[0];
var values = from value in source
select new DummyInterfaceImplementor()
{
A = value.A,
B = value.C + "_" + value.D
};
DoSomethingWithDummyInterface(values.Cast<IDummyInterface>());
}
public void DoSomethingWithDummyInterface(IEnumerable<IDummyInterface> values)
{
foreach (var value in values)
{
Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
}
}
}
Note that you need to cast the result of the query to the type of the interface. There might be a better way to do it, but I couldn't find it.
The answer to the question specifically asked is no. But have you been looking at mocking frameworks? I use MOQ but there's millions of them out there and they allow you to implement/stub (partially or fully) interfaces in-line. Eg.
public void ThisWillWork()
{
var source = new DummySource[0];
var mock = new Mock<DummyInterface>();
mock.SetupProperty(m => m.A, source.Select(s => s.A));
mock.SetupProperty(m => m.B, source.Select(s => s.C + "_" + s.D));
DoSomethingWithDummyInterface(mock.Object);
}
Another option is to create a single, concrete implementing class that takes lambdas in the constructor.
public interface DummyInterface
{
string A { get; }
string B { get; }
}
// "Generic" implementing class
public class Dummy : DummyInterface
{
private readonly Func<string> _getA;
private readonly Func<string> _getB;
public Dummy(Func<string> getA, Func<string> getB)
{
_getA = getA;
_getB = getB;
}
public string A => _getA();
public string B => _getB();
}
public class DummySource
{
public string A { get; set; }
public string C { get; set; }
public string D { get; set; }
}
public class Test
{
public void WillThisWork()
{
var source = new DummySource[0];
var values = from value in source
select new Dummy // Syntax changes slightly
(
getA: () => value.A,
getB: () => value.C + "_" + value.D
);
DoSomethingWithDummyInterface(values);
}
public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
{
foreach (var value in values)
{
Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
}
}
}
If all you are ever going to do is convert DummySource to DummyInterface, then it would be simpler to just have one class that takes a DummySource in the constructor and implements the interface.
But, if you need to convert many types to DummyInterface, this is much less boiler plate.
Using Roslyn, you can dynamically create a class which inherits from an interface (or abstract class).
I use the following to create concrete classes from abstract classes.
In this example, AAnimal is an abstract class.
var personClass = typeof(AAnimal).CreateSubclass("Person");
Then you can instantiate some objects:
var person1 = Activator.CreateInstance(personClass);
var person2 = Activator.CreateInstance(personClass);
Without a doubt this won't work for every case, but it should be enough to get you started:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace Publisher
{
public static class Extensions
{
public static Type CreateSubclass(this Type baseType, string newClassName, string newNamespace = "Magic")
{
//todo: handle ref, out etc.
var concreteMethods = baseType
.GetMethods()
.Where(method => method.IsAbstract)
.Select(method =>
{
var parameters = method
.GetParameters()
.Select(param => $"{param.ParameterType.FullName} {param.Name}")
.ToString(", ");
var returnTypeStr = method.ReturnParameter.ParameterType.Name;
if (returnTypeStr.Equals("Void")) returnTypeStr = "void";
var methodString = #$"
public override {returnTypeStr} {method.Name}({parameters})
{{
Console.WriteLine(""{newNamespace}.{newClassName}.{method.Name}() was called"");
}}";
return methodString.Trim();
})
.ToList();
var concreteMethodsString = concreteMethods
.ToString(Environment.NewLine + Environment.NewLine);
var classCode = #$"
using System;
namespace {newNamespace}
{{
public class {newClassName}: {baseType.FullName}
{{
public {newClassName}()
{{
}}
{concreteMethodsString}
}}
}}
".Trim();
classCode = FormatUsingRoslyn(classCode);
/*
var assemblies = new[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(baseType.Assembly.Location),
};
*/
var assemblies = AppDomain
.CurrentDomain
.GetAssemblies()
.Where(a => !string.IsNullOrEmpty(a.Location))
.Select(a => MetadataReference.CreateFromFile(a.Location))
.ToArray();
var syntaxTree = CSharpSyntaxTree.ParseText(classCode);
var compilation = CSharpCompilation
.Create(newNamespace)
.AddSyntaxTrees(syntaxTree)
.AddReferences(assemblies)
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
var result = compilation.Emit(ms);
//compilation.Emit($"C:\\Temp\\{newNamespace}.dll");
if (result.Success)
{
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
var newTypeFullName = $"{newNamespace}.{newClassName}";
var type = assembly.GetType(newTypeFullName);
return type;
}
else
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
return null;
}
}
}
public static string ToString(this IEnumerable<string> list, string separator)
{
string result = string.Join(separator, list);
return result;
}
public static string FormatUsingRoslyn(string csCode)
{
var tree = CSharpSyntaxTree.ParseText(csCode);
var root = tree.GetRoot().NormalizeWhitespace();
var result = root.ToFullString();
return result;
}
}
}

Categories

Resources