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);
}
}
Related
I have a project that stores data of "custom" types and each of these types is represented as a class. I am able to collect these types from assembly and create instances of these when I need to in run time:
var type = typeof(IDataPointListableCore);
var availableTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(w => w.IsAbstract == false)
.Where(x => type.IsAssignableFrom(x));
...
IDataPointListableCore c = (IDataPointListableCore)Activator.CreateInstance(item, "");
It worked fine but an obvious disadvantage of this approach is that if I want to add a new type I have to rebuild and deploy the application. So I decided to externalize these classes into .ddl assemblies that I would be loading from a folder on application start.
A sample type would look like this:
namespace DataPoints
{
public class DataPointRAG : IDataPoint<string>
{
private enum AcceptedValues { none, red, amber, green };
public string ParsedValue { get; private set; } = null;
public string Name { get; } = "nRAG";
public string Description { get; } = "One of the four RAG indicators: None, Red, Amber, Green";
public string Value { get; private set; }
public DataPointRAG(string _v)
{
Value = _v;
}
public Type GetExpectedValueType()
{
return typeof(string);
}
public string GetParsedValue()
{
return ParsedValue;
}
public ValidationResult Validate()
{
if (Enum.IsDefined(typeof(AcceptedValues), Value.ToLower()))
{
ParsedValue = Value.ToLower();
return new ValidationResult(true, "OK");
}
return new ValidationResult(false,
string.Format("Given value ({0}) does not correspond to any of the accepted values {1} for this data point type",
Value,
Enum.GetValues(typeof(AcceptedValues)).ToString()));
}
}
}
And it implements two interfaces (I reference a project with these):
namespace DataPoints
{
public interface IDataPointListable
{
string Name { get; }
string Description { get; }
string ParsedValue { get; }
string Value { get; }
ValidationResult Validate();
}
}
namespace DataPoints
{
public interface IDataPoint<T> : IDataPointListable
{
System.Type GetExpectedValueType();
T GetParsedValue();
}
}
The problem is that when I load the assembly and try to instantiate this class, it throws an InvalidCastException: can't cast DataPoints.DataPointRAG to IDataPointListable
public static List<Assembly> Asses = new List<Assembly>();
...
public static void Main(string[] args)
{
var _loggerFactory = new LoggerFactory()
.AddConsole(LogLevel.Debug)
.AddDebug();
var _logger = _loggerFactory.CreateLogger("Startup");
string[] files = Directory.GetFiles(DATAPOINTSFOLDER, "*.dll");
foreach (string f in files)
{
Assembly DataPointsAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(f);
Asses.Add(DataPointsAssembly);
}
foreach (Type _assType in Asses.SelectMany(x => x.GetTypes()))
{
_logger.LogDebug("Found {0} in {1}", _assType, _assType.FullName);
}
foreach (Type _assType in Asses.SelectMany(x => x.GetTypes()).Where(x=>typeof(IDataPointListable).IsAssignableFrom(x)))
{
_logger.LogDebug("Found matching assemblies");
var point = (IDataPointListable)Activator.CreateInstance(_assType, "");
_logger.LogDebug("{0} successfully initiated", point.Name);
}
BuildWebHost(args).Run();
}
It does not recognize any of the found types as Assignable to IDataPointListable:
log:
[40m[37mdbug[39m[22m[49m: Startup[0]
Found DataPoints.DataPointRAG in DataPoints.DataPointRAG
[40m[37mdbug[39m[22m[49m: Startup[0]
Found DataPoints.ValidationResult in DataPoints.ValidationResult
[40m[37mdbug[39m[22m[49m: Startup[0]
Found DataPoints.IDataPoint`1[T] in DataPoints.IDataPoint`1
[40m[37mdbug[39m[22m[49m: Startup[0]
Found DataPoints.IDataPointListable in DataPoints.IDataPointListable
[40m[37mdbug[39m[22m[49m: Startup[0]
Found DataPoints.DataPointRAG+AcceptedValues in DataPoints.DataPointRAG+AcceptedValues
but if I try something like this (just to load the type based on the class name since I know it implements the proper interface) - I will get the InvalidCastException:
string[] files = Directory.GetFiles(DATAPOINTSFOLDER, "*.dll");
foreach (string f in files)
{
Assembly DataPointsAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(f);
foreach(Type _t in DataPointsAssembly.GetTypes()){
_logger.LogDebug("Type {0} in Assembly {1}", _t, DataPointsAssembly.FullName);
if (_t.ToString() == "DataPoints.DataPointRAG"){
var x = (IDataPointListable)Activator.CreateInstance(_t, "");
_logger.LogDebug("Activated class of type {0}", x.GetType());
}
}
if I don't cast to IDataPointListable then it will generate an instance of the DataPointRAG and the public class fields will be in there but it will be as if the class implemented no interfaces at all.
Is there something I am missing from how things work when loaded from external assembly?
The structure of my project is:
Core->References DataPointTemplate project (DataPoints namespace)
DataPointTemplate project provides the two interfaces and
ValidationMessage class Data PointRAG project->References DatapointTemplate project, but same namespace
I am using Roslyn and I have the following class:
var source = #"
using System;
class MyClass : MyBaseClass {
static void Main(string[] args) {
Console.WriteLine(""Hello, World!"");
}
}";
// Parsing
SyntaxTree tree = CSharpSyntaxTree.ParseText(source);
// This uses an internal function (working)
// That gets the first node of type `SimpleBaseTypeSyntax`
SimpleBaseTypeSyntax simpleBaseType = GetNBaseClassNode(tree);
Getting the base class name
I successfully get access to node SimpleBaseTypeSyntax which contains what I need. In fact, if I use the syntax explorer, I get:
Node IdentifierToken has everything I need has its Text, Value and ValueText properties are "MyBaseClass"!
However, while in the syntax explorer I can see all these values, I cannot access them programmatically.
So I try retrieving the node programmatically:
IdentifierNameSyntax identifierNode =
simpleBaseType.ChildNodes().OfType<IdentifierNameSyntax>().First();
SyntaxToken identifier = simpleBaseType.Identifier;
string name = identifier.Text;
But name is empty string. Same as identifier.Value and identifier.ValueText.
What am I doing wrong? Maybe I am doing wrong, so how would you retrieve the base class name?
Another attempt: Using the semantic model
I started thinking that I need the semantic model for this type of information:
IdentifierNameSyntax identifierNode =
simpleBaseType .ChildNodes().OfType<IdentifierNameSyntax>().First();
SemanticModel semanticModel =
CSharpCompilation.Create("Class")
.AddReferences(MetadataReference.CreateFromFile(
typeof(object).Assembly.Location))
.AddSyntaxTrees(tree).GetSemanticModel(tree);
SymbolInfo symbolInfo = this.semanticModel.GetSymbolInfo(identifierNode);
string name = symbolInfo.Symbol.Name;
This throws exception as symbolInfo.Symbol is null.
I actually don't know why you can't pass the BaseTypeSyntax to the semantic model via GetSymbolInfo(), but it's also returning null for me with no errors.
Anyways, here's an approach that works:
var tree = CSharpSyntaxTree.ParseText(#"
using System;
class MyBaseClass
{
}
class MyClass : MyBaseClass {
static void Main(string[] args) {
Console.WriteLine(""Hello, World!"");
}
}");
var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
var myClass = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().Last();
var myClassSymbol = model.GetDeclaredSymbol(myClass) as ITypeSymbol;
var baseTypeName = myClassSymbol.BaseType.Name;
You'll want to use the semantic model here because you won't be able to reliably tell if you're dealing with an interface or a base type at the syntax level.
I can see that you're trying to build an analyzer with the Roslyn API.
You do know that there are other ways to test your analyzer logic? Using unit test files instead of directly having the source inside the analyzer.
Using this idea, you entirely build your analyzer with the template provided by Visual Studio, where you must inherit from DiagnosticAnalyzer and you create your analysis code logic.
For your situation, you should look at ClassDeclaration and easily access the BaseTypes property inside the Node.
public bool SomeTriedDiagnosticMethod(SyntaxNodeAnalysisContext nodeContext)
{
var classDeclarationNode = nodeContext.Node as ClassDeclarationSyntax;
if (classDeclarationNode == null) return false;
var baseType = classDeclarationNode.BaseList.Types.FirstOrDefault(); // Better use this in all situations to be sure code won't break
var nameOfFirstBaseType = baseType.Type.ToString();
return nameOfFirstBaseType == "BaseType";
}
protected static bool IsWebPage(SyntaxNodeAnalysisContext context, ClassDeclarationSyntax classDeclaration)
{
INamedTypeSymbol iSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration) as INamedTypeSymbol;
INamedTypeSymbol symbolBaseType = iSymbol?.BaseType;
while (symbolBaseType != null)
{
if (symbolBaseType.ToString() == "System.Web.UI.Page")
return true;
symbolBaseType = symbolBaseType.BaseType;
}
return false;
}
This is my helper class that finds all properties, class name, class namespace and base classes.
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace MyProject
{
public class CsharpClass
{
public string Name { get; set; }
public string Namespace { get; set; }
public List<CsharpProperty> Properties { get; set; }
public List<string> BaseClasses { get; set; }
public class CsharpProperty
{
public string Name { get; set; }
public string Type { get; set; }
public CsharpProperty(string name, string type)
{
Name = name;
Type = type;
}
}
public CsharpClass()
{
Properties = new List<CsharpProperty>();
BaseClasses = new List<string>();
}
}
public static class CsharpClassParser
{
public static CsharpClass Parse(string content)
{
var csharpClass = new CsharpClass();
var tree = CSharpSyntaxTree.ParseText(content);
var members = tree.GetRoot().DescendantNodes().OfType<MemberDeclarationSyntax>();
foreach (var member in members)
{
if (member is PropertyDeclarationSyntax property)
{
csharpClass.Properties.Add(new CsharpClass.CsharpProperty(
property.Identifier.ValueText, property.Type.ToString()));
}
if (member is NamespaceDeclarationSyntax namespaceDeclaration)
{
csharpClass.Namespace = namespaceDeclaration.Name.ToString();
}
if (member is ClassDeclarationSyntax classDeclaration)
{
csharpClass.Name = classDeclaration.Identifier.ValueText;
csharpClass.BaseClasses = GetBaseClasses(classDeclaration).ToList();
}
//if (member is MethodDeclarationSyntax method)
//{
// Console.WriteLine("Method: " + method.Identifier.ValueText);
//}
}
return csharpClass;
}
private static IEnumerable<string> GetBaseClasses(ClassDeclarationSyntax classDeclaration)
{
if (classDeclaration == null)
{
return null;
}
if (classDeclaration.BaseList == null)
{
return null;
}
return classDeclaration.BaseList.Types.Select(x => x.Type.ToString());
}
}
}
Usage:
const string content = #"
namespace Acme.Airlines.AirCraft
{
public class AirCraft
{
public virtual string Name { get; set; }
public virtual int Code { get; set; }
public AirCraft()
{
}
}
}";
var csharpClass = CsharpClassParser.Parse(content);
Console.WriteLine(csharpClass.Name);
Console.WriteLine(csharpClass.Namespace);
Console.WriteLine(csharpClass.Properties.Count);
Console.WriteLine(csharpClass.BaseClasses.Count);
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
//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
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;
}
}
}