I have classes which are derived from a base class and would like to instantiate these classes dynamically at run time. Using the following code, I can build a class using the default constructor:
public abstract class Employee : IEmployeeInterface
{
public string Name { get; set; }
public Employee() { }
public Employee(string name)
{
Name = name;
}
}
public static class EmployeeBot
{
static readonly Func<string, Func<Employee>> EmployeeBotFunction = x =>
Expression.Lambda<Func<Employee>>(
Expression.New(Type.GetType(x).GetConstructor(Type.EmptyTypes))
).Compile();
public static Employee InstantiateNewEmployee(string type)
{
string argumentType = string.Format("{1}.{0}", type.ToString(), MethodBase.GetCurrentMethod().DeclaringType.Namespace);
return EmployeeBotFunction(argumentType).Invoke();
}
}
Worker implements Employee and can be created using:
Employee employee = EmployeeBot.InstantiateNewEmployee("Worker");
Then, since Worker has all the same methods as Employee, calling them will give the results from the worker class as expected.
However, I cannot work out how to correctly implement similar code to use a constructor with arguments. For example:
static readonly Func<string, Type[], Func<string, Employee>> NewEmployee = (x, y) =>
Expression.Lambda<Func<string, Employee>>(
Expression.New(Type.GetType(x).GetConstructor(y))
).Compile();
public static Employee InstantiateNewEmployee(string type, string Name)
{
Type[] construct = new Type[] { typeof(string) };
string argumentType = string.Format("{1}.{0}", type.ToString(), MethodBase.GetCurrentMethod().DeclaringType.Namespace);
return NewEmployee(argumentType, construct).Invoke(Name);
}
Calling this method throws an exception:
EmployeesTests.InstantiateEmployeeWithName threw exception:
System.InvalidOperationException: Method may only be called on a Type
for which Type.IsGenericParameter is true.
How can the NewEmployee function be altered to accept the required parameters for the second constructor?
I have answered this with the help of #JSteward so thank you!
Combined with a different version of Expression.New and fixing a stupid mistake I was able to solve this. You cannot instance an abstract class so my constructor should have been on one of the derived classes, e.g public class Worker : Employee and not public abstract class Employee.
The actual error was that the constructor parameter to the Expression.New(Type.GetType(x).GetConstructor(y)); was null because it was not defined on the Worker.
The second example now becomes; (though I'm sure this can be improved!).
static readonly Func<string, Type[], ParameterExpression[], Func<string, Employee>> NewEmployee = (x, y, z) =>
Expression.Lambda<Func<string, Employee>>(
Expression.New(Type.GetType(x).GetConstructor(y), z)
, z).Compile();
public static Employee InstantiateNewEmployee(string type, string Name)
{
Type[] construct = new Type[] { typeof(string) };
string argumentType = string.Format("{1}.{0}", type.ToString(), MethodBase.GetCurrentMethod().DeclaringType.Namespace);
ParameterExpression[] arguments = new ParameterExpression[] { Expression.Parameter(typeof(string)) };
return NewEmployee(argumentType, construct, arguments).Invoke(Name);
}
The post from #JSteward 's comments actually generalises this requirement already: linq-expressions-creating-objects
I appreciate your help and also the comment from the blog.
But good to see that I’m not the only person crazy enough to be using Expressions in this way!
Related
I am currently creating a custom way of deep-copying my objects. I use a static class for this functionality.
public static class CopyServer
{
public static int CopyDeep(int original)
{
return original;
}
//not shown: same for all other value types I use (long, float,...)
public static T CopyDeep<T>(T original) where T: ICopyAble
{
if (original == null)
return default;
if (original is ICopyAutofields)
return CopyAutofields(original);
return (T)original.CopyDeep();
}
private static T CopyAutofields<T>(T original)
{
Delegate del;
if (!_copyFunctions.TryGetValue(typeof(T), out del))
{
//not shown: Building expression for parameter etc.
foreach (var fieldInfo in typeof(T).GetFields())
{
//not shown: checking options set by custom attributes
MethodInfo methodInfo = typeof(CopyServer).GetMethod("CopyDeep", new[] { fieldInfo.FieldType });
//I can't remove the second param without getting an AmbiguousMatchException
if (methodInfo == null)
{
throw new Exception($"CopyDeep not defined for type {fieldInfo.FieldType}");
}
if (methodInfo.IsGenericMethod)
methodInfo = methodInfo.MakeGenericMethod(fieldInfo.FieldType);
Expression call = Expression.Call(methodInfo, readValue);
//not shown: Assign Expression
}
//not shown: return Expression and compiling
}
return ((Func<T, T>)del)(original);
}
}
I use T CopyAutofields<T> to build functions (by building and compiling Expression Trees) so I don't have to create copy-functions for each and every class I want to copy by hand. I control the copy-behaviour with Custom Attributes (I left this part in the code above since it is not relevant for my problem).
The code works fine as long as long as only fields with types for which a non-generic function exists are used. But it is not able to retrieve my generic function T CopyDeep<T>.
Example:
//This works:
public class Manager : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
}
//This doesn't
//Meaning: typeof(CopyServer).GetMethod("copyDeep", new[] { fieldInfo.FieldType });
//in T copyAutofields<T> returns null for the Manager-field and my exception gets thrown
public class Employee : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
public Manager Manager;
}
//This is what I was using before I started using the ICopyAutofields.
//This approach works, but its' too much too write since my classes usually
//have way more than three fields and I occasionally forget to update
//copyDeep()-function if I add new ones.
public class Employee : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
public Manager Manager;
public IModable CopyDeep()
{
var result = new Employee();
result.FirstName = CopyServer.copyDeep(FirstName);
result.LastName= CopyServer.copyDeep(LastName);
result.Manager= CopyServer.copyDeep(Manager);
return result;
}
}
Long story short: I need a way of getting a matching function for a type T if both generic and non-generic functions with the right name exist.
In .NET 4.7.1 you need to use method GetMethods and filter the results:
class MyClass
{
public T M<T>(T t) { return default(T); }
public int M(int t) { return 0; }
}
var m = typeof(MyClass).GetMethod("M", new[] { typeof(string) }); // null
var m1 = typeof(MyClass).GetMethods()
.Where(mi => mi.Name == "M" && mi.GetGenericArguments().Any())
.First(); // returns generic method
In .NET Standard 2.1 (and .NET Core since 2.1) there is another way to resolve generic type arguments - Type.MakeGenericMethodParameter, like you can see it in this answer.
Also as workaround you can move your copyAutofields<T> method to generic class like CopyAutoFieldServer<T>:
public static class CopyAutoFieldServer<T>
{
public static T copyAutofields(T original) { ... }
}
My code is as follows:
public partial class WhereHelper<T1> { }
public static partial class WhereHelperExtension
{
public static T Where<T,T1>(this T t, Expression<Func<T1,bool>> where) where T : WhereHelper<T1>
{
//do something....
return t;
}
}
public class Test
{
public void Main()
{
WhereHelper<DateTime> dt = new WhereHelper<DateTime>();
dt.Where(t => t.Year == 2016);//this is error
dt.Where<WhereHelper<DateTime>, DateTime>(t => t.Year == 2016);//this is success
}
}
Extension method I want to be like this:
WhereHelper<DateTime> dt = new WhereHelper<DateTime>();
dt.Where(t => t.Year == 2016);//this is error
how to create generic extension with Expression method.
Visual Studio does not recognize the "Where" extension methods.
In C#, if you need to provide any generic argument, you must provide them all. where constraints do not provide hints to the type resolver, and so it's impossible to decide what T1 is.
Change your signature to the following:
public static WhereHelper<T> Where<T>(this WhereHelper<T> t, Expression<Func<T,bool>> where)
{
return t;
}
Here, we know exactly what T, purely from the first argument, and so we do not have to explicitly specific the arguments.
I'm trying to get an instance value of a generic type from a static field inside a generic class, and it throws this exception:
Late bound operations cannot be performed on fields with types for which Type.ContainsGenericParameters is true
public class ManagerTemplate<Q, T> : IObjectManager
where T : Filmation.Runtime.Engine.ObjectId, new( )
where Q : ManagerTemplate<Q, T>, new( ) {
public readonly static Q Instance = new Q( ); <---- STATIC FIELD
}
private static void FindManagers( ) {
var IObjectManagerType = typeof( IObjectManager );
var managers = IObjectManagerType.Assembly
.GetTypes( )
.Where( t => !t.IsAbstract && t.GetInterfaces().Any( i => i == IObjectManagerType) );
foreach (var manager in managers) {
var fi = manager.GetField( "Instance" );
var instance = fi.GetValue( null ); <--- EXCEPTION
}
}
I have tried to use GetGenericTypeDefinition, but continues throwing the exception.
I have searched in google, but I have not found how it can be done...
Anyone knows how it can be done?
EDIT: The same using a static property
This is the workaround that I have implemented, (though I'd like to know if it can be done using reflection):
public static Q Instance { get; private set; }
static ManagerTemplate( ) {
Instance = new Q( );
Environment.Managers.Add( Instance );
}
You cannot get the value of public readonly static Q Instance = new Q( ); from the Generic Type Definition ManagerTemplate<Q, T>, simply because there is no concrete type for Q.
How do you get an instance of the generic type definition Q if you do not yet know what the concrete type for Q is? Simple: You can't.
Now... if what you want to get is an instance of a type that descends from ManagerTemplate<Q, T> where the generic type parameter Q is defined, then you actually want to exclude Generic Type Parameters from your search.
private static IEnumerable<IObjectManager> FindManagers()
{
Type type = typeof(IObjectManager);
IEnumerable<Type> managers = type.Assembly
.GetTypes()
.Where(t => !t.IsAbstract && t.GetInterfaces().Contains(type));
foreach (Type manager in managers)
{
var fi = manager.GetField("Instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
if (fi != null &&
!fi.FieldType.IsGenericParameter &&
type.IsAssignableFrom(fi.FieldType))
{
yield return (IObjectManager) fi.GetValue(null);
}
}
}
This will get you all the "Managers" defined in all classes that descend from ManagerTemplate<Q, T> that have defined what the type Q is.
The problem is that you are trying to get the Instance field from an unbound generic type, that is, a generic type with unspecified type arguments. An unbound type cannot be instantiated or its methods be called. You need a bound generic type with all the type parameters specified, but consider that each different concrete bound type won't share static fields. For example ManagerTemplate<Class1, Class2> will return a different Instance that ManagerTemplate<Class1, Class3>. All instances of ManagerTemplate<Class1, Class2> will share static fields, though.
You can use reflection to bind type arguments to unspecified type parameters of unbound generic types with Type.MakeGenericType. You will need to provide type arguments to your FindManagers class:
private static void FindManagers<Q,T>( ) {
var IObjectManagerType = typeof( IObjectManager );
var managers = IObjectManagerType.Assembly
.GetTypes( )
.Where( t => !t.IsAbstract && t.GetInterfaces().Any( i => i == IObjectManagerType) );
foreach (var manager in managers) {
var concreteType = manager.MakeGenericType(typeof(Q), typeof(T));
var fi = concreteType.GetField( "Instance" );
var instance = fi.GetValue( null );
}
}
I would solve the problem like the following way. Let's start from the code, and I will then explain why the code:
public interface IObjectManager {
}
public abstract class ObjectManager: IObjectManager {
static IEnumerable<IObjectManager> ManagerInstancesIterator() {
foreach(var managerType in managerTypes) {
var info=managerType.BaseType.GetField("Instance");
var instance=info.GetValue(null) as IObjectManager;
yield return instance;
}
}
public static IObjectManager[] FindManagerInstances() {
return ManagerInstancesIterator().ToArray();
}
public ObjectManager() {
managerTypes.Add(this.GetType());
}
static readonly HashSet<Type> managerTypes=new HashSet<Type>();
}
public class ManagerTemplate<Q, T>: ObjectManager
where T: new()
where Q: ManagerTemplate<Q, T>, new() {
public readonly static Q Instance=new Q();
}
public class CuriousClass<T>
: ManagerTemplate<CuriousClass<T>, T> where T: new() {
}
the test code:
public static partial class TestClass {
public static void TestMethod() {
var instanceByObject=CuriousClass<object>.Instance;
var instanceByInt32=CuriousClass<int>.Instance;
var instances=ObjectManager.FindManagerInstances();
}
}
Note the constraint of Filmation.Runtime.Engine.ObjectId is removed temporarily, you can add it back as your requirement.
You are using the curiously recurring template pattern, and the consumers' code in fact does not have a way to instantiate ManagerTemplate<Q, T> without implementing a concrete class which inherits from ManagerTemplate<Q, T>.
For the reason you encountered the exception, is pointed out by other answers. The class ManagerTemplate<Q, T> is an open generic class, that is, unless you specified the type arguments, the generic class is just a definition; it doesn't have a type instance.
The close generic type would be in the runtime types cache, and doesn't exist in the assembly. So the easiest way to get the type instances which are really used to instantiate objects, is store them in a collection. But as ManagerTemplate<Q, T> is generic, if we declare a collection in its class declaration, then there would be different collections for each close generic type. This is the reason why a base class ObjectManager. But, we don't want itself be instantiated, so it's abstract.
For why managerTypes.Add(this.GetType()); is in an instance constructor instead of a class initializer, the reason is simple, a static constructor doesn't let us know what type is this.
So in conclution, I consider a design in this way as a practical solution.
It is possible to do this:
public static void SomeMethod<TFunc>(Expression<TFunc> expr)
{
//LambdaExpression happily excepts any Expession<TFunc>
LambdaExpression lamb = expr;
}
and call it elsewhere passing a lambda for the parameter:
SomeMethod<Func<IQueryable<Person>,Person>>( p=>p.FirstOrDefault());
I would instead like to pass an expression as a parameter to an attribute constructor.
Is it possible to do the below?
class ExpandableQueryAttribute: Attribute {
private LambdaExpression someLambda;
//ctor
public ExpandableQueryMethodAttribute(LambdaExpression expression)
{
someLambda = expression
}
}
//usage:
static LambdaExpression exp =
(Expression<Func<IQueryable<Person>, Person>>)
(p => p.FirstOrDefault());
[ExpandableQueryAttribute(exp)] //error here
// "An attribute argument must be a constant expression, typeof expression
// or array creation expression of an attribute parameter type"
My goal is to specify a method or lambda in the constructor of the attribute(even if I have to declare a full named method and pass the name of the method somehow, that'd be fine to).
Parameter types can change, but it is important that the attribute constructor can take that parameter and in some way be able to assign it to a field of type LambdaExpression
I want the declaration of the lambda/method to be just above the call to the attribute constructor, or inline, so that you don't have to go far to see what is being passed.
So these alternatives would be fine, but no luck getting them to work:
public static ... FuncName(...){...}
[ExpandableQueryAttribute(FuncName)]
// ...
or
//lambdas aren't allowed inline for an attribute, as far as I know
[ExpandableQueryAttribute(q => q.FirstOrDefault())]
// ...
The existing work around is to pass a number ID to the constructor(satisfying the "argument must be a constant" requirement), which is used by the constructor to do a lookup in a dictionary where expressions have been added previously. Was hoping to improve/simplify this, but I have a feeling it doesn't get any better due to limitations on attribute constructors.
how about this:
class ExpandableQueryAttribute : Attribute
{
private LambdaExpression someLambda;
//ctor
public ExpandableQueryAttribute(Type hostingType, string filterMethod)
{
someLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null);
// could also use a static method
}
}
this should let you assign your lambda to a field and then suck it in at runtime, although in general I would prefer to use something like PostSharp to do this at compile time.
simple usage example
public class LambdaExpressionAttribute : Attribute
{
public LambdaExpression MyLambda { get; private set; }
//ctor
public LambdaExpressionAttribute(Type hostingType, string filterMethod)
{
MyLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null);
}
}
public class User
{
public bool IsAdministrator { get; set; }
}
public static class securityExpresions
{
public static readonly LambdaExpression IsAdministrator = (Expression<Predicate<User>>)(x => x.IsAdministrator);
public static readonly LambdaExpression IsValid = (Expression<Predicate<User>>)(x => x != null);
public static void CheckAccess(User user)
{
// only for this POC... never do this in shipping code
System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
var method = stackTrace.GetFrame(1).GetMethod();
var filters = method.GetCustomAttributes(typeof(LambdaExpressionAttribute), true).OfType<LambdaExpressionAttribute>();
foreach (var filter in filters)
{
if ((bool)filter.MyLambda.Compile().DynamicInvoke(user) == false)
{
throw new UnauthorizedAccessException("user does not have access to: " + method.Name);
}
}
}
}
public static class TheClass
{
[LambdaExpression(typeof(securityExpresions), "IsValid")]
public static void ReadSomething(User user, object theThing)
{
securityExpresions.CheckAccess(user);
Console.WriteLine("read something");
}
[LambdaExpression(typeof(securityExpresions), "IsAdministrator")]
public static void WriteSomething(User user, object theThing)
{
securityExpresions.CheckAccess(user);
Console.WriteLine("wrote something");
}
}
static void Main(string[] args)
{
User u = new User();
try
{
TheClass.ReadSomething(u, new object());
TheClass.WriteSomething(u, new object());
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
This is not possible because what you can pass into an attribute needs to fit into the CLR's binary DLL format and there is no way to encode arbitrary object initialization. For the same you you cannot pass a nullable value for example. The restrictions are very strict.
Although you cannot have complex constructor for attributes, in some situation a work-aound is to have a public property for that attribute and update it at run-time.
self point to an object of the class that contains some attributes on its properties. LocalDisplayNameAttribute is a custom attribute.
The following code will set ResourceKey property of my custom attribute class at run-time. Then at that point you can override DisplayName to outpiut whatever text you want.
static public void UpdateAttributes(object self)
{
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(self))
{
LocalDisplayNameAttribute attr =
prop.Attributes[typeof(LocalDisplayNameAttribute)]
as LocalDisplayNameAttribute;
if (attr == null)
{
continue;
}
attr.ResourceKey = prop.Name;
}
}
Use dymanic linq: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
You can make the attribute's constructor take a string which evaluates to an expression.
I have this delegate declaration:
public delegate IEnumerable<T> SearchInputTextStrategy<T, U>(string param);
Lets assume I did create here the new SearchInputTextStrategy delegate and name it MyDelegate.
this is my method declaration:
public void BindElements<T, TDisplayProperty,TSortProperty>
(
IEnumerable<T> dataObjects,
Func<T, TDisplayProperty> selectorDisplayMember,
Func<T, TSortProperty> selectorSortMember,
string delimiter,
// 1.) how to declare the delegate here as parameter ??
)
{
// pass here the delegate to a private field to save it
// 2.) how can I do that?
}
How can I do 1.) and 2.) ? :-)
UPDATE 2:
Ok thats what I have done so far:
public class SearchProvider<T>
{
public delegate IEnumerable<T> SearchInputTextStrategy<T>(string param);
public SearchInputTextStrategy<T> SearchStrategy { get; set; }
public T TypedValue
{
get
{
return (T)Convert.ChangeType(SearchStrategy, typeof(T));
}
}
}
UserControl:
public delegate IEnumerable<T> SearchInputTextStrategy<T>(string param);
public void BindElements<T, TDisplayProperty,TSortProperty>
(
IEnumerable<T> dataObjects,
Func<T, TDisplayProperty> selectorDisplayMember,
Func<T, TSortProperty> selectorSortMember,
string delimiter,
SearchInputTextStrategy<T> searchStrategy
)
{
/// assign the searchStrategy to the SearchProvider class
var sp = new SearchProvider<T>();
sp.SearchStrategy = searchStrategy // DOES NOT WORK !!!
}
Please read also my comments in the Code. What I want to achieve is pass the delegate to the searchProvider to save it somewhere... The code I write here I understand up to 50 % so please bear with me Generics are new to me although I use generic List for a long time ;P
UPDATE 2:
public partial class MainWindow : Window
{
public delegate IEnumerable SearchInputTextStrategy(string param);
private SearchInputTextStrategy<ICustomer> _strategy;
public MainWindow()
{
InitializeComponent();
IEnumerable<ICustomer> customers = DataService.GetCustomers();
_strategy = new SearchInputTextStrategy<ICustomer>(SearchCustomers);
ElementUserControl.BindElements(customers, c => c.FirstName, c => c.SortId, ";", _strategy);
namespace ElementTextBoxV2
{
public partial class MainWindow : Window
{
public delegate IEnumerable<ICustomer> SearchInputTextStrategy<ICustomer>(string param);
private SearchInputTextStrategy<ICustomer> _strategy;
public MainWindow()
{
InitializeComponent();
IEnumerable<ICustomer> customers = DataService.GetCustomers();
_strategy = new SearchInputTextStrategy<ICustomer>(SearchCustomers);
ElementUserControl.BindElements(customers, c => c.FirstName, c => c.SortId, ";", _strategy);
IEnumerable<ICustomer> selectedElements = ElementUserControl.SelectedElements<ICustomer>();
}
// Just a Test-Methode to assure the delegate works
public IEnumerable<ICustomer> SearchCustomers(string param)
{
IEnumerable<ICustomer> foundCustomers = new List<ICustomer>();
return foundCustomers;
}
}
}
The scenario is, that the user has put the TextBoxUserControl in a MainWindow and he has to pass a delegate pointing to a searchMethod. I have implemented this with the SearchCustomers_Method. The problem is that C# can not resolve that:
Error 1 The best overloaded method match for 'ElementTextBoxV2.ElementsView.BindElements<ElementTextBoxV2.ICustomer,string,int>(System.Collections.Generic.IEnumerable<ElementTextBoxV2.ICustomer>, System.Func<ElementTextBoxV2.ICustomer,string>, System.Func<ElementTextBoxV2.ICustomer,int>, string, ElementTextBoxV2.Provider.SearchInputTextStrategy<ElementTextBoxV2.ICustomer>)' has some invalid arguments
Error 2 Argument 5: cannot convert from 'ElementTextBoxV2.MainWindow.SearchInputTextStrategy<ElementTextBoxV2.ICustomer>' to 'ElementTextBoxV2.Provider.SearchInputTextStrategy<ElementTextBoxV2.ICustomer>'
Do you see the problem? In any case the User must pass a delegate with the same definition the BindElements Method has!
It's odd that your SearchInputTextStrategy has two type parameters but only actually uses one... but you just need to specify the type arguments in the parameter type. For example:
public void BindElements<T, TDisplayProperty,TSortProperty>
(
IEnumerable<T> dataObjects,
Func<T, TDisplayProperty> selectorDisplayMember,
Func<T, TSortProperty> selectorSortMember,
string delimiter,
SearchInputTextStrategy<T, TDisplayProperty> searchStrategy
)
I've only guessed at what the type arguments should be - you haven't really said what you want the parameter to represent.
You won't be able to easily have a field of the right type in your class, because the class itself doesn't know the type parameters involved. It's possible that you should really be making your class generic, or make another class which is able to handle the delegates appropriately. Without any more information, it's very hard to know which.
private SearchInputTextStrategy<T, string> _searchStrategy;
public void BindElements<T, TDisplayProperty,TSortProperty>
(
IEnumerable<T> dataObjects,
Func<T, TDisplayProperty> selectorDisplayMember,
Func<T, TSortProperty> selectorSortMember,
string delimiter,
SearchInputTextStrategy<T, string> searchStrategy
)
{
_searchStrategy = searchStrategy;
}