Dynamically pass class and method names to a method using generics - c#

I want to pass in dynamically a class name and a method name to a method and keep this dynamic, I'm understanding that I should use generics and possible constraints.
Example, I have a class
MemberRequestDTO (contains several properties)
I also that a Method called
RecordsToRetrieve
Using some reflection I was wanting to dynamically get the values of the properties, which I figured out how to do that, but then I realized that is is too hard code and tightly coupled of which I figured time to refactor and create a method with a signature that uses generics with constraints. having trouble with understand the use of and the constraints etc..
So I want to pass in a class name and be able to use it in the method, with reflection I plan to use it like:
Type type = typeof(classname);
I started reading and researching and I start playing with code like this:
public void GetTypeValues<T>() where T : class , new()
How do I pass in the class name of MemberRequestDTO?
What does the Generic new for me?
How do I pass a class name into the parens ()?
If I use does it also get pass into parens?
How can I pass in class and method?
Reading the above "Where T has the constraints (enforced) to be of type "class AND new() ?
A little lost and confused on this, forgive me.
EDIT:
Based on the answers and some research, I'm understanding this a bit more:
Lets forget about me trying to pass in a method, say I just want to pass in a class
Say the class with properties looks like this
public class MemberRequestDTO
{
public DateTime DateRequested { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Then I will New this up
var memberRequestDTO = new MemberRequestDTO();
Then I want to pass this to class into a method that is generic
How do I go about passing an instance of a object into a generic method? What about the signature , example public void GetTypeValues() where T : class , new()
Would I want to have the contraints of class and new() ?
For the above, is T the instance of the class? Thus the purpose is that I can be
Saying
GetTypeValues(memberRequestDTO)
( this is my actual question , pass into whatever class I instantiated and that let the method "handle" dealing with that class with looping through the properties and getting me the name values of the properties dynamically and yes it probably will not remain a void method )
Should passing in memberRequestDTO be with quotes or without? I want to be able to pass in any instance of a class to the member to then manipulate it more. () should T be there ? should the parens () be empty or contain an generic parameter for the class object ?

Here are your answers:
GetTypeValues<MemberRequestDTO>()
new() is a constraint for the Type Parameter - T. It says that the type argument T must have a public parameterless constructor. In your case, MemberRequestDTO class must a public parameterless constructor like below:
public class MemberRequestDTO
{
public MemberRequestDTO() { ... }
}
As a class name is of reference type, you can pass it as a type into the parens like: SomeMethod(typeof(MemberRequestDTO)); where the signature of the method be void SomeMethod(Type type) { }
If you pass the class as a type parameter as in point (1), it does not get passed into the parens()
class constraint implies that "The type argument must be a reference type; this applies also to any class, interface, delegate, or array type."
and new() constraint implies that "The type argument must have a public parameterless constructor. When used together with other constraints, the new() constraint must be specified last."
EDIT:
If I catch your point, then the generic method definition would be something like:
public void GetTypeValues<T>(T typeObject) where T : class
{
// typeObject specific operations
}
That uses typeObject dynamically, getting the "execution-time compiler" to perform type inference to work out T. See the reference here. Moreover, imho, you don't need the new () constraint on T here.
After that, you can pass an instance of any class to this method like below:
var memberRequestDTO = new MemberRequestDTO();
GetTypeValues((dynamic) memberRequestDTO);
EDIT 2:
USAGE: Get Type Values dynamically using Reflection
This method returns the property values wrapping into IEnumerable<KeyValuePair<string, object>>.
public static IEnumerable<KeyValuePair<string, object>> GetTypeValues<T>(T typeObject) where T : class
{
// typeObject specific operations
IEnumerable<KeyValuePair<string, object>> typeValues =
typeObject
.GetType()
.GetProperties()
.Select(property => new KeyValuePair<string, object>(property.Name, property.GetValue(typeObject)));
return typeValues;
}

How do I pass in the class name of MemberRequestDTO ?
You already have one. In a generic method "Type parameter" in this case T will be the name of type you're interested in.
public void GetTypeValues<T>() where T : class , new()
{
string typeName = typeof(T).Name;
}
What does the Generic new for me?
It is a contraint which will prevent you to pass any type without public parameterless constructor. In other words it will allow you to new up type passed in as "Type parameter"
public void GetTypeValues<T>() where T : class , new()
{
T instance = new T();//This is not possible without new constraint
}
How do I pass a class name into the parens () ?
If I use does it also get pass into parens?
Not sure what is that parens() Need more info to answer this.
How can I pass in class and method?
If I understand correctly "Type parameter" T is the runtime type which you use. So you get a Type there. Am not sure about what you mean by class? Class cannot be passed only instances can be passed.
For methods there are number of ways. You can pass MethodInfo or method name or A delegate, or a MethodCallExpression etc.
Reading the above "Where T has the constraints (enforced) to be of
type "class AND new() ?
Yes. class constraint prevents you from passing value types, new() constraint allows you to new up things.
Read more about generics here and here

I'm a little confused about what you want to do but I'll give it a shot. I can see two possible interpretations and they differ on what the caller is starting with and what you're trying to achieve.
Interpretation #1: The caller starts out knowing the name of the class and the name of the method it wants to invoke later, using an object it has in hand. This can be achieved as follows:
public Func<object, object> RecordMethod(string typeName, string methodName)
{
var type = Type.GetType(typeName);
var method = type.GetMethod(methodName);
return (object o) => method.Invoke(o, new object[0]);
}
var method = RecordMethod("MemberRequestDTO", "RecordsToRetrieve");
// later that day ...
MemberRequestDTO someObj = ...;
var result = method.Invoke(someObj);
This is fine if you need to work with type names and method names dynamically, e.g. from user input. Note that this approach requires the use of object throughout, and will only work with a method that takes no parameters. Also note that in this way the type cannot be guaranteed to have a no-arg constructor, so the caller must provide the object himself.
Interpretation #2: The caller starts out knowing the actual class and the actual method it wants to invoke later, using an object that can be constructed later. This can be achieved as follows:
public Func<TOutput> CaptureMethod<TInput, TOutput>(Func<TInput, TOutput> method)
where TInput : new()
{
return () =>
{
var source = new TInput();
return method(source);
};
}
var capturedMethod = (MemberRequestDTO dto) => dto.RecordsToRetrieve();
// later that day ...
var result = capturedMethod();
This captures a known method and returns a function which, when invoked, will instantiate your class and call the method on it. This is a more static approach (the caller knows more than in the previous example) and is able to enforce a constraint that the type being worked with must have a no-arg constructor.
I don't know if I've answered your question but this should at least give you some ideas.

Related

Generic method cast T to object type

I have a method that accepts a generic type of BaseViewModel.
I want to get the actual object in the method.
What I tried:
public static void LogScreen<T>() where T : BaseViewModel
{
var viewModel = T as BaseViewModel;
}
You are not passing any object into the method, only the parameter type.
To apply value correctly, You should use the syntax like:
public static void LogScreen<T>(T TArg)
where T : BaseViewModel
{
var viewModel = TArg; //not necessary: as BaseViewModel;
}
In this kind of code You do not really need to use generic and You might go with simplier version as:
public static void LogScreen(BaseViewModel viewModel)
{
//already got viewModel as correct type and checked during compile for types
}
Note: The big advantage of using generic approach is putting there some type and then work with the type whole time (great example IEnumerable<int>). If You would do that without generic type, You would need to use object everywhere and You would need to cast the object all the time
Generic is advantage there as You can put anything inside.
On the other hand if You have a method, where You have only one type the only one, it is easier to go with strict parameter type (2nd approach).
Comment of ZoharPeled - the code should be written as:
public static void LogScreen<T>(T TArg)
where T : BaseViewModel
{
var viewModel = TArg as BaseViewModel;
var viewModel2 = TArg;
}
From the compiler's point of view, the type of the reference named viewModel is exactly the same as the type of the reference named viewModel2 - both are BaseViewModel, and both instances may or may not be of any type derived from BaseViewModel. Long story short, the as operator is redundant in this code. Even if you pass an instance of some class derived from BaseModelView you can only reference it as a BaseModelView in this method.

Has it a more "beautiful" solution for a generic determination?

I have the following code which works but I think it may not beoptimal because in theory compiler can determinate a generic type from the calling child class. Is there a way to rewrite this code such that I do not need to provide the ChildType generic type parameter?
public abstract class Test<ChildType, T> where ChildType: Test<ChildType, T>, new()
{
public T Field { get; set; }
public static ChildType Get(T field) {
return new ChildType() { Field = field };
}
}
public class ChildTest: Test<ChildTest, string>
{
}
// Call sample
var child = ChildTest.Get("test");
The answer is that it is pretty much required for the Test class to have the ChildType generic type specified. You could use reflection to get the current type and instantiate an instance of it but reflection is generally best avoided.
To see why consider the Test class on its own and in particular this line:
return new ChildType() { Field = field };
That line requires two pieces of information. First it needs to know what the actual type of ChildType is. You can't hardcode it because it can of course vary. The second piece of information it needs is to know it can create a new object of this type in this way.
Both of these pieces of information are provided through the generic type parameter and the generic constraint which is why it is required.

How to return a dynamic object from a generic function - error

I have two generic functions. In the first one I fill a dictionary and then call the next generic function to convert the dictionary to object.
Here I need to return to the T generic object rather than specifying to a particular one. I am not able to achieve this. It shows error:
The type T must be a reference type in order to use it as a parameter "T" in the generic method or type..
public T Fill<T>()
{
Dictionary<string,string> d = new Dictionary<string,string>();
//filled dictionary -----
SomeClass dObject = ToObject<SomeClass>(d);
//[---Here I need to return a dynamic object rather than fixing to SomeClass--]
T dObject = ToObject<T>d); ///* Not able to acheive this *///
return (T)Convert.ChangeType(dObject, typeof(T));
}
private T ToObject<T>(IDictionary<string, string> dict)
where T : class,new()
{
T t = new T();
PropertyInfo[] properties = t.GetType().GetProperties();
//--- code to convert object to dictionary
return t;
}
Because the ToObject method is constrained with class and new(), you also need to match that to your Fill method.
public T Fill<T>() where class, new()
However, there doesn't appear to be a need for the class constraint so you can remove it if you like.
In this line of code you have a constraint that T must be a class and it must have a parameterless constructor (new):
private T ToObject<T>(IDictionary<string, string> dict)
where T : class,new()
Your Fill method's signature is like this:
public T Fill<T>()
See there are no constraints which means I can pass a struct or any other type to it, even a type which does not have a parameterless constructor. This is why you are getting the error. You need to define the same constraint or something more specific than the constraint you have defined on ToObject<T> method. To fix your issue, simply do this:
public T Fill<T>() where class, new()
Now Fill<T> has the same constraint so I can only call it with a class who has a parameterless constructor.

Access to properties of an object Func<S, T>

I do not know the full operation of Func<T, TResult>, so maybe my question is simple or obvious to some of you.
I have the following header of a function:
private static T MyMethod<S, T>(Func<S, T> testFunc) where S : class, new()
{
//Code....
}
I want to access to the properties from S object. Possible?
For example: S.name
Sorry for my english.
First of all, you should be aware that in your method, you don't have an S object. You have a function that takes an S and returns a T. In this case, I would assume that your method is responsible for making the S instances and passing them to testFunc as parameters.
However, it is possible for you to get access to the type information of S, using reflection:
private static T MyMethod<S, T>(Func<S, T> testFunc) where S : class, new()
{
var typeofS = typeof(S);
var s = new S();
var defaultName = (string)(typeofS.GetProperty("Name").GetValue(s, null));
}
(I hope its obvious but, if you get an S that doesn't have a Name property, the reflection will fail; production code should of course be doing a lot more null checking, exception handling, etc.)
Can you write:
private static T MyMethod<S, T>(Func<S, T> testFunc) where S : S, new()
{
//Code....
}
With S who are you class type ? Or your abstract class type where you already have some common properties ?
All your method knows about an S is that it's a class and can be instantiated. It does not know if it has a property called name. You may need to require that parameter to inherit from an interface or class that has the expected properties.
You could also try casting to a know type, understanding that the cast may fail if the object is not of that type.

How to define generic extension method that returns type of sub generic

I have a definition like this:
public static IQueryable<D> ReturnDTO<E, D>(this IQueryable<E> query)
where D : BaseDTO, new()
where E : BaseObjectWithDTO<D, int>
{
//expression tree code to convert
}
BaseObjectWithDTO defines what type it's DTOs are. Hence I would have thought the by defining E I would have been also defining D.
But IQueryable.ReturnDTO() requires that the generic parameters be specified like this:
IQueryable.ReturnDTO<someEntity, someDTO>();
Which is obviously UGLY.
I tried making this IQueryable<E> as this IQueryable<BaseObjectWithDTO<D, int>> instead but then this has nothing as the in of the func because it won't take a type inferred by the Generic Parameter of the IQuerayble:
var projection = Expression.Lambda<Func<E, D>>(memberInitExpression, itemParam);
Ideas on how to get this to not require the types be passed every time?
Unfortunately, C#'s generic type inference system isn't as powerful as it could be. If you include a parameter involving D, then it can infer it. For example...
public static IQueryable<D> ReturnDTO<E, D>(this IQueryable<E> query,
IQueryable<BaseObjectWithDTO<D, int>> dummy)
// now you can do...
myQueryable.ReturnDTO(myQueryable);
// instead of
myQueryable.ReturnDTO<BaseObjectWithDTO<BaseDTO, int>, BaseDTO>();
It's confusing and arguably a poor design to pass the same variable in twice, but it's better (IMHO) than having to explicitly specify the types or resort to reflection or other runtime techniques to extract the types (when that's otherwise unnecessary).
Since you aren't actually going to use the dummy parameter, it doesn't matter what the value is, as long as the type is right, so you might still be able to use this at the end of a query chain, e.g. this will still return the expected value, even though you pass in two different IQueryables.
var result = otherQueryable.Where(...).ReturnDTO(otherQueryable);
If you prefer to be slightly less cryptic, you could make the dummy parameter D dummy, and then e.g. myQueryable.ReturnDTO(default(SomeDTO)) (here using default as a clear way of getting a null or default value without having a reference to a variable/field/property of that type) if you prefer.
I don't think it is possible as you currently have it designed, this MSDN page states that type inference is not possible in this scenario:
The same rules for type inference apply to static methods and instance
methods. The compiler can infer the type parameters based on the
method arguments you pass in; it cannot infer the type parameters only
from a constraint or return value.
That means you have to pass in a parameter of your type to this method for the compiler to be able to infer the types.
You have to specify the type, but it doesn't have to be done explicitly in the q.Return<E,D>(). There are ways that you can pass specify the type parameter so that it can be inferred implicitly. To do that, you'll need to change the signature a bit.
public static IQueryable<D> ReturnDTO<E, D>(this IQueryable<E> query, D dtoTypeExample = default(D))
where D : BaseDTO, new()
where E : BaseObjectWithDTO<D, int>
{
//expression tree code to convert
}
Now, even though there's a default parameter, the compiler won't be able to get it unless you pass some argument in. The thing you pass in doesn't have to be used by the method in any other way though. For example, assume you have:
public class ProductDTO : BaseDTO {
public static ProductDTO Empty { get { return new ProductDTO(); } }
}
public class Product : BaseObjectWithDTO<ProductDTO,int> {
public static IQueryable<Product> QuerySource { get; set; }
}
You could then call:
ProductDTO dto = Product.QuerySource.ReturnDTO(ProductDTO.Empty);
I'm not saying that this is necessarily a good idea, but you could do it. Also, it doesn't have to be the actual type that you pass in - you just need to pass in something that's close enough for the compiler to infer the intended type. For example, you could have a signature like:
public static IQueryable<D> ReturnDTO<E, D>(this IQueryable<E> query, Func<D,D> dtoIdentity = default(Func<D,D>))
where D : BaseDTO, new()
where E : BaseObjectWithDTO<D, int>
{
//expression tree code to convert
}
then if you have:
public class ProductDTO : BaseDTO {
public static ProductDTO Identity(ProductDTO dto){ return dto; };
}
public class Product : BaseObjectWithDTO<ProductDTO,int> {
public static IQueryable<Product> QuerySource { get; set; }
}
You could then call:
ProductDTO dto = Product.QuerySource.ReturnDTO(ProductDTO.Identity);
This might make more semantic sense to some, but it's somewhat subjective. Once again, I'm not recommending this, just saying that you can do it. If you do decide to do it though, it might save you a little work to have a self-referential generic base (Warning: Eric Lippert discourages this kind of thing). But anyway, your design would then look like:
public abstract class BaseDTO<T> where T : BaseDTO<T>, new()
{
public static T Empty { get { return new T(); } }
}
public class ProductDTO : BaseDTO<ProductDTO> { }
You could also add the type constraint to your ReturnDTO method if you want to enforce an invariant that all DTOs were then self-referential derivatives of BaseDTO<T> with public parameterless constructors. But, if you're trying to write what would conventionally be considered good code you probably won't do any of this and you'll just close your eyes and explicitly use the parameter constraint if you think it's ugly.
There is one other thing I thought of, which wouldn't be so frowned upon. Think about the Queryable.Cast<T> and Queryable.OfType<T> methods. They take a non generic IQueryable parameter but returns an IQueryable<T>. If you make sure to validate your assumptions about the parameter, it's probably clean enough. Then you would lose some compile-time type-safety though. You would need to have a non-generic base like BaseObjectWithDTO that BaseObjectWithDTO<TData,TKey> would inherit from. Your method would then look like:
public static IQueryable<D> ReturnDTO<D>(this IQueryable<BaseObjectWithDTO> query)
where D : BaseDTO, new()
{
if(query == null) throw new ArgumentNullException("query");
if( !typeof(BaseObjectWithDTO<D,int>) .IsAssignableFrom(query.GetType().GetGenericParameters()[0]))
throw new ArgumentOutOfRangeException("query");
//expression tree code to convert
}
That's not terrible. But it might not be good either. It's probably better than the other options I listed, but who knows.
Another syntax that might work for you just occurred to me, but it's also pretty abusive. Imagine you did go the BaseDTO<T> where T : BaseDTO<T>,new() route. You could declare the method on that type to extract the DTO queryable. This is what I'm thinking:
public abstract class BaseDTO<T>
where T : BaseDTO<T>, new()
{
public static T From(BaseObjectWithDTO<T,int> entity){
if(entity == null) throw new ArgumentNullException("entity");
//expression tree code to convert
}
}
then you don't really need that method ReturnDTO as an extension method anymore, because you have normal LINQ. You could still add it as syntactic sugar if you want, but using these semantics instead, your call ends up looking like:
IQueryable<ProductDTO> dtoQuery = from entity in Product.QuerySource select ProductDTO.From(entity);
which can also be written as
Product.QuerySource.Select(entity => ProductDTO.From(entity));
and if you were using an IEnumerable instead of an IQueryable could be
Product.QuerySource.Select(ProductDTO.From);
Please remember: All I'm saying is that you can do things this way. I'm not saying you should.

Categories

Resources