How to get name of a class property? - c#

Is there anyway I can get the name of class property IntProperty?
public class ClassName
{
public static int IntProperty { get { return 0; } }
}
//something like below but I want to get the string of "IntProperty"
ClassName.IntProperty.GetType().Name
Basically what I want to do is to dynamically save property name string into the database, and later on retrieve it from the database and invoke the property dynamically.
Seems like what I am looking for is similar to duck typing I think.
Thanks!
UPDATED:
This is the actual code. This is more like a workflow kind of thing. But each task is defined as property of a class (class is used to group tasks).
public class ApplicationTask
{
public static Task<string> SendIncompleteNotification
{
get
{
return new Task<string>
(
a => Console.WriteLine("Sample Task")
, "This is a sample task which does nothing."
);
}
}
}
So, the code will be able to retrieve the full name of the class and property something like: namespace.ApplicationTask.SendIncompleteNotification and save this into the database. Later on, the code will read the string and dynamically create the task and pass it into another to execute.

With C#6.0 you can get it by
nameof(ClassName.IntProperty)

I think that the use of the GetProperty method in this case, is redundant, because you need to know the property name to call the method.
You could loop through your properties and extract its name:
foreach (PropertyInfo p in typeof(ClassName).GetProperties())
{
string propertyName = p.Name;
//....
}

The result of ClassName.IntProperty is just an integer value. As soon as it's executed and the result is returned, there's no trace of it having come from IntProperty.
If you're using .NET 3.5 you can use an expression tree instead, usually created via a lambda expression:
Expression<Func<int>> exp = () => ClassName.IntProperty;
You can then compile and execute the expression and separately find out what it's doing (retrieving IntProperty in this case). I'm not really sure whether this is suitable for what you want to do though.
If you do work out how to save the property name in the database, then GetProperty is the way to go on the retrieval front.
Perhaps if you could give more context in the question in terms of how you want to use this, we could help more. You've shown just an expression - if you could show it in terms of where you'd be using it, that would be great.
EDIT: You've expanded the property, but not how it's being called. Do you need to call it directly, rather than just fetching the list of properties using Type.GetProperties and storing the list of property names in the database?
Again, if you could show the code which calls the property, and how you want it to interact with the database, we may be able to make more progress.

Type objectType = this.GetType();
PropertyInfo property = objectType.GetProperty("intProperty");
System.Console.Write(property.Name);
Is this what you need?

You can simply use nameof(ClassName.IntProperty)
It will give you "IntProperty"

I came across this, and it seems very helpful for getting property name. (C++)
#define getVarName(varName,holder) sprintf(holder, "%s", #varName)
int main() {
int var = 100; // just any type of identifier
char name[100]; // it will get the variables name
getVarName(var, name);
puts(name);
return 0;
}
ref: http://zobayer.blogspot.com/2010/05/c-fun-get-variables-name.html

Related

Is there a way to make a IEnumerable method that lets me return any list type in c#?

So currently, you can imagine I have 1 method that is the constructor that funcitons like
info.PersonalInfo=getPersonalInfo(Id);
info.MedicalInfo=getMedicalInfo(Id);
Thing is, all of those get data and get binarys are repeating 95% of the code
using (CVDataEntities data = new CVDataEntities())
{
var temp = data.PersonalInfo.Where(m => m.Id == Id).FirstOrDefault();
return temp;
}
The only thing that changes is instead of PersonalInfo its MedicalInfo.
I thought of using a switch and just sending a number as the selector for which specific object I would want.
But the problem is the method is made so that it can only return
public IEnumerable<PersonalInfo> getPersonalInfo (string Id)
Is there any way for me to make a IEnumerable that lets me return any object, or is there a better way to go about this. I want to do it mostly to reduce the code from 400 lines down to 200 at most.
Try using generic methods, you will be able to specify the return type of your function when you call it. This could make your code look like this :
public IEnumerable<T> getInfo<T>(string id)
{
// Some code
}
// Calling the function
info.PersonalInfo = getInfo<PersonalInfo>(Id);
info.MedicalInfo = getInfo<MedicalInfo>(Id);
But be careful while using it, cause the compiler won't know what type T is (it is only defined at runtime) so it could lead to some errors while processing the data (like missing properties / methods exclusive to a specific type)
EDIT : Johnathan Barclay made a good point by commenting that the // some code bit is relevant and asked "How would the correct property be selected on data? How do you access an Id property on T?"
To get the correct property and access an Id property, you could use System.Reflection and add a string parameter to get the name of the property you want to use, and another to give the Id property name to the function:
public IEnumerable<T> getInfo<T>(string id, string propertyToReadName, string propertyToCompareName)
{
using (CVDataEntities data = new CVDataEntities())
{
// Getting the enumerable not filtered first
IEnumerable<T> unfilteredList = (IEnumerable<T>)data.GetType() // Get the type
.GetProperty(propertyToReadName, typeof(T)) // Get the property (PersonalInfo or MedicalInfo)
.GetValue(data); // Get the value of this property in the `data` instance
// Filtering the list
IEnumerable<T> filteredList = unfilteredList.Where(m =>
typeof(T).GetProperty(propertyToCompareName) // Get the "id" property using parameter
.GetValue(m) // Get the "id" value of m instance
.Equals(id)); // Check if it equals the id given as parameter
return filteredList;
}
}
// Calling the function
info.PersonalInfo = getInfo<PersonalInfo>(Id, "PersonalInfo", "Id");
info.MedicalInfo = getInfo<MedicalInfo>(Id, "MedicalInfo", "Id");
If you want to return a single element instead of an IEnumerable don't forget to change the return type of the function from IEnumerable<T> to T and add .FirstOrDefault() at the return line
Note that you could also give another value to the parameter propertyToCompareName and make a comparison to another property of the T class

Detect access to data from a method in c#

I have a method and I want to find all data accessed inside this method.
for example:
public class foo
{
private int field;
public void method()
{
field = 0;
}
}
I need a way to know (from the source code or the assembly) that 'field' is accessed within 'method'.
Note: I already used Harmony Library (https://github.com/pardeike/Harmony) to find all the calls to methods inside a specific method. It would be amazing if someone knows how to use it to also find data accessed.
as pointed out in comments, Roslyn DataFlowAnalysis is probably the best tool for the job:
SyntaxTree tree = CSharpSyntaxTree.ParseText(
#"public class foo
{
private int field;
public void method()
{
field = 0;
}
}");
var compilation = CSharpCompilation.Create("test").AddSyntaxTrees(tree).AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location));
var model = compilation.GetSemanticModel(tree);
var methodDeclaration = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().First();
var dataFlow = model.AnalyzeDataFlow(methodDeclaration.Body);
foreach (var symbol in dataFlow.DataFlowsIn)
{
Console.WriteLine(symbol.Name, symbol.Kind);
}
analysis for your particular example yields reference to class foo through implicit
this, but not the field itself (which is technically correct but not very helpful). Assuming your actual code will likely have a bit more dependencies in it - you may see better results.
If you look at the list of opcodes in IL you find that you want to search for OpCodes.Ldfld and OpCodes.Ldflda. Harmony’s CodeInstruction has two fields to use: compare code to either opcode and use operand and cast it to a FieldInfo to learn about what field is read. Use OpCodes.Stfld(a) for write access to fields.

Dynamically get a DbSet<T> by Entity class name

I'm trying to use System.Reflections to get a DbSet<T> dynamically from its name.
What I've got right now is:
The DbSet name
The DbSet's Type stored on a variable
The issue I'm facing comes out when trying to use the dbcontext.Set<T>() method, since (these are my tries so far):
When I try to assign to <T> my DbSet Type, it throws me the following compilation error:
"XXX is a variable but is used like a type"
If I try with using both the Extension methods that you will find below in my code (which I made in order to try to get an IQueryable<T>), it returns a IQueryable<object>, which unfortunately is not what I am looking for, since of course when I try to manipulate it with further Reflections, it lacks of all the properties that the original class has…
What am I doing wrong? How can I get a DbSet<T>?
My code is the following, but of course, let me know if you need more infos, clarifications or code snippets.
My Controller's Method:
public bool MyMethod (string t, int id, string jsonupdate)
{
string _tableName = t;
Type _type = TypeFinder.FindType(_tableName); //returns the correct type
//FIRST TRY
//throws error: "_type is a variable but is used like a type"
var tableSet = _context.Set<_type>();
//SECOND TRY
//returns me an IQueryable<object>, I need an IQueryable<MyType>
var tableSet2 = _context.Set(_type);
//THIRD TRY
//always returns me am IQueryable<object>, I need an IQueryable<MyType>
var calcInstance = Activator.CreateInstance(_type);
var _tableSet3 = _context.Set2(calcInstance);
//...
}
Class ContextSetExtension
public static class ContextSetExtension
{
public static IQueryable<object> Set(this DbContext _context, Type t)
{
var res= _context.GetType().GetMethod("Set").MakeGenericMethod(t).Invoke(_context, null);
return (IQueryable<object>)res;
}
public static IQueryable<T>Set2<T>(this DbContext _context, T t)
{
var typo = t.GetType();
return (IQueryable<T>)_context.GetType().GetMethod("Set").MakeGenericMethod(typo).Invoke(_context, null);
}
}
EDIT Added TypeFinder's inner code.
In brief, this method does the same of Type.GetType, but searches Type on ALL the generated assemblies
public class TypeFinder
{
public TypeFinder()
{
}
public static Type FindType(string name)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
var result = (from elem in (from app in assemblies
select (from tip in app.GetTypes()
where tip.Name == name.Trim()
select tip).FirstOrDefault())
where elem != null
select elem).FirstOrDefault();
return result;
}
}
UPDATE as requested in the comments, here's the specific case:
In my DB i've got some tables which are really similar each other, so the idea was to create a dynamic table-update method which would be good for every table, just passing to this method the table name, the ID of the row to update and the JSON containing data to update.
So, in brief, I would perform some updates on the table given in input as DbSet type, updating the row with ID==id in input with the data contained inside the JSON, which will be parsed inside an object of type X(the same of dbset)/into a dictionary.
In pseudo-code:
public bool MyMethod (string t, int id, string jsonupdate)
{
string _tableName = t;
Type _type = TypeFinder.FindType(_tableName); //returns the correct type
//THIS DOESN'T WORKS, of course, since as said above:
//<<throws error: "_type is a variable but is used like a type">>
var tableSet = _context.Set<_type>();
//parsing the JSON
var newObj = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonupdate, _type);
//THIS OF COURSE DOESN'T WORKS TOO
//selecting the row to update:
var toUpdate = tableSet.Where(x => x.Id == id).FirstOrDefault();
if(toUpdate!=null)
{
var newProperties = newObj.GetType().GetProperties();
var toUpdateProperties = toUpdate.GetType().GetProperties();
foreach(var item in properties)
{
var temp = toUpdateProperties.Where(p => p.Name==item.Name)
{
//I write it really in briefand fast, without lots of checks.
//I think this is enough, I hope
temp.SetValue(toUpdate, item.GetValue());
}
}
_context.SaveChanges();
}
return false;
}
returns me an IQueryable<object>, I need an IQueryable<MyType>
Well, that will never work. Your IQueryable cannot be of type IQueryable<MyType>because that would mean the compiler would need to know what MyType is and that is not possible, because the whole point of this exercise is to decide that on runtime.
Maybe it's enough to know that those objects are in fact instances of MyType?
If not, I think you have painted yourself into a corner here and you are trying to figure out what paint to use to get out of there. Take a step back, it's probably not a technical problem. Why do you need to do this? Why do you have the conflicting needs of knowing the type at runtime only and knowing it at compile time?
You need to think about your requirements, not about the technical details.
I needed to dynamically load a single record from the database for each type in a list of known types, to print a test email when an admin is editing the template, so I did this:
List<object> args = new List<object>();
//...
//other stuff happens that isn't relevant to the OP, including adding a couple fixed items to args
//...
foreach (Type type in EmailSender.GetParameterTypes())
{
//skip anything already in the list
if (args.Any(a => a.GetType().IsAssignableFrom(type))) continue;
//dynamically get an item from the database for this type, safely assume that 1st column is the PK
string sql = dbContext.Set(type).Sql.Replace("SELECT", "SELECT TOP 1") + " ORDER BY 1 DESC";
var biff = dbContext.Set(type).SqlQuery(sql).AsNoTracking().ToListAsync().Result.First();
args.Add(biff);
}
Caveat: I know at least one record will exist for all entities I'm doing this for, and only one instance of each type may be passed to the email generator (which has a number of Debug.Asserts to test validity of implementation).
If you know the record ID you're looking for, rather than the entire table, you can use dbContext.Set(type).Find(). If you want the entire table of whatever type you've sussed out, you can just do this:
string sql = dbContext.Set(type).Sql; //append a WHERE clause here if needed/feasible, use reflection?
var biff = dbContext.Set(type).SqlQuery(sql).ToListAsync().Result;
Feels a little clunky, but it works. There is strangely no ToList without Async, but I can run synchronously here. In my case, it was essential to turn off Proxy Creation, but you look like you want to maintain a contextful state so you can write back to db. I'm doing a bunch of reflection later, so I don't really care about strong typing such a resulting collection (hence a List<object>). But once you have the collection (even just as object), you should be able to use System.Reflection as you are doing in your UPDATE sample code, since you know the type and can use SetValue with known/given property names in such a manner.
And I'm using .NET Framework, but hopefully this may translate over to .NET Core.
EDIT: tested and working:
public async Task<bool> MyMethod(string _type)
{
Type type = Type.GetType(_type);
var tableSet = _context.Set(type);
var list = await db.ToListAsync();
// do something
}
// pass the full namespace of class
var result = await MyMethod("Namespace.Models.MyClass")
IMPORTANT NOTE: your DbContext need to have the DbSet declared to work!
public class MyContext : DbContext
{
public DbSet<MyClass> MyClasses { get; set; }
}

Is it possible to know a variable's name [duplicate]

This question already has answers here:
How can I get the name of a variable passed into a function?
(23 answers)
Closed 8 years ago.
One thing to note is: I need to get the variable's name, not the property's name.
I have a scenario where I need to pass in either List<int> or List<double> to a view. So in the partial view I bind the model to dynamic:
#model dynamic
var nameOfParameter = Web.Controllers.MemberInfoGetting.GetMemberName(() => Model);
#foreach (var value in list)
{
<td>#Html.Editor(value, "DoubleTemplate", new { Column = count, Switcher = (YearOfProgram >= count)})</td>
sum += (double)value;
++count;
}
And this is how I call the partial view from the main view:
#Html.Partial("_myPartial", Model.CategoryList)
Then I found that I have to know the name(CategoryList) of the list which is passed into the partial view.
Here I found many posts talking about using something like this:
public static class MemberInfoGetting
{
public static string GetMemberName<T>(Expression<Func<T>> memberExpression)
{
MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
return expressionBody.Member.Name;
}
}
}
This code doesn't work for me as the result of (nameOfParameter )calling it will always be "Model", rather than "CategoryList".
So, is it possible to achieve what I want?
Update1
The reason why I need to pass in the name of the list is because I need the name of the list to form the name on the html element into something like: CategoryList_1. So Razor knows I am trying to bind the value of the textbox into the 1st element of a property in my view model.
class MyViewModel
{
................
public List<double> CategoryList {get; set;}
................
}
Update2
#Html.RenderPartial("_VerificationSummarySection",new { ListName = "PracticeEvaluationCreditsVerifiedList", List = Model.PracticeEvaluationCreditsVerifiedList})
I'm now trying to pass in the name of the list by using RenderPartial. But, I cannot find the right way to use it.
Names are relative. The list itself doesn't have a name.
One thing to notice is : I need to get the variable's name, not the property's name.
In your use of the method:
var nameOfParameter = Web.Controllers.MemberInfoGetting.GetMemberName(() => Model);
The name here is Model, and Model is the property; it isn't a variable. You have obtained the name of the property: "Model". You cannot, however, obtain the name as it would have been in a calling context (even in regular C# this is hard; between views, however, it is essentially impossible).
As an aside, with that GetMemberName method, if you did use it to obtain the name of a variable, in IL terms it would actually cease being a variable, and would instead become a field - because that is how "captured variables" are implemented by the compiler.
You should instead make it possible to pass down the name you want; either as additional context on the view, or as a view-model that encapsulates a list and a name, or by creating a "named list" concept (perhaps by subclassing List<T> or similar, or by adding an INamedList interface).

How do I select a C# variable using a string

I have a string user setting and want to select a particular variable with the same name during the startup of my C# Windows app.
e.g.
I have a user setting (string) called UserSelectedInt, which is currently set to 'MyTwo'. (Please note that my variables are actually a lot more complex types than integers, I've just used them as examples.)
public static int MyOne = 12345;
public static int MyTwo = 54321;
public static int MyThree = 33333;
public int myInt = SelectMyVariableUsing(MyApp.Settings.Default.UserSelectedInt)
The user may have selected 'MyTwo' last time they closed the app, so that is the variable I want to select during startup. I hope I'm making sense.
Please can some let me know how would I achieve this?
Thanks
Use a Dictionary<string, int>. That allows you to assign an integer value to a number of strings. If the string is user input, you need to check that it is valid before trying to retrieve the corresponding integer.
Sounds like you are trying to implement the provider pattern. You may find using this is a better mechanism for you to use, especially as you say it is more complex than using ints.
In your code you would reference the particular provider using your MyApp.Settings.Default.UserSelectedInt setting.
I would say architecturally this would be a better mechanism than some of the other suggested answers.
// enumerate your list of property names (perhaps from a file)
var settings = new List<string>();
settings.Add("MyOne");
settings.Add("MyTwo");
settings.Add("MyThree");
var settingMap = new Dictionary<string, int>();
int value = 0;
foreach (var name in settings)
{
try
{
// try to parse the setting as an integer
if (Int32.TryParse((string)Properties.Settings.Default[name], out value))
{
// add map property name to value if successful
settingMap.Add(name, value);
}
else
{
// alert if we were unable to parse the setting
Console.WriteLine(String.Format("The settings property \"{0}\" is not a valid type!", name));
}
}
catch (SettingsPropertyNotFoundException ex)
{
// alert if the setting name could not be found
Console.WriteLine(ex.Message);
}
}
However, if you get to the stage where your variable list is HUGE then I would maybe look at actually accessing the properties file directly via some form of XML parsing.
The simplest way is probably to just use GetField.
Using your example just change the last line to:
var selectedField = MyApp.Settings.Default.UserSelectedInt
public int myInt = (int) GetType().GetField(selectedField).GetValue(this);
If the field or class is static the syntax should be:
var selectedField = MyApp.Settings.Default.UserSelectedInt
public int myInt = (int)typeof(YourClass).GetField(selectedField).GetValue(null);
See http://msdn.microsoft.com/en-us/library/system.reflection.fieldinfo.getvalue.aspx for more details.
Should be possible with Reflection
You could also use a Hashtable.
However, from the looks of it all your really wanting to do is store the value the user last selected hence I would probably just save it in the UserSettings as a string and then on load I would just parse the value.
I hope I am reading this right, but essentially, I feel you want to remember the value user selected last time. Since the application will be shutdown in the meantime, you are going to have to store it somewhere. Maybe in a file.
Too me it sounds more like you should use an enumeration in stead of static variables, consider the following example:
public enum MyVars
{
MyOne = 12345, MyTwo = 54321, MyThree = 33333
}
static void Main(string[] args)
{
Console.WriteLine(String.Format("Name:{0}, Val={1}", MyVars.MyOne.ToString(), (int)MyVars.MyOne ));
Console.ReadKey();
}
, it will output "Name:MyOne, Val=12345", with enumerations you can easily pick out the name of the variable.

Categories