I'm working on Unity, using C#, and I made up a script that would make my life simpler if I could access constants using string variables.
public class Foo
{
public const string FooConst = "Foo!";
public const string BarConst = "Bar!";
public const string BazConst = "Baz!";
}
// ...inside some method, somewhere else
public string Bar(string constName)
{
// is it possible to do something like this?
// perhaps with reflections?
return Foo.GetConstant(constName);
}
My only solution was to create a method that gets the constant inside a switch. But every time I add a new constant, I have to modify that switch.
Fun fact: I'm a PHP kid that moved into C#. I like it is pretty strict, strong-typed and stuff... but that also makes things unnecessarily complicated.
This uses reflection:
var value = typeof ( Foo ).GetFields().First( f => f.Name == "FooConst" ).GetRawConstantValue();
You could certainly do that using reflection, but IMHO a better option would be to store the constants in a dictionary or some other data structure. Like so:
public static class Foo
{
private static Dictionary<string,string> m_Constants = new Dictionary<string,string>();
static Foo()
{
m_Constants["Foo"] = "Hello";
// etc
}
public static string GetConstant( string key )
{
return m_Constants[key];
}
}
public string Bar( string constName )
{
return Foo.GetConstant( constName );
}
Obviously this is a simplification. And it would throw an exception if you pass a key that doesn't exists etc.
you could try in this way with reflection
var constExample= typeof(Foo).GetFields(BindingFlags.Public | BindingFlags.Static |
BindingFlags.FlattenHierarchy)
.Where(fi => fi.IsLiteral && !fi.IsInitOnly && fi.Name==constName).FirstOrFefault();
where constName is the constant that you are looking for
Refere here for documentation about property of FieldInfo.
As you can see I have filtered for IsLiteral = true and IsInitOnly = false
IsLiteral:
Gets a value indicating whether the value is written at compile time
and cannot be changed.
IsInitOnly:
Gets a value indicating whether the field can only be set in the body
of the constructor.
Yes, you have to use Reflection. Like this:
public string Bar(string constName)
{
Type t = typeof(Foo);
return t.GetField(constName).GetValue(null));
}
Related
I have to following C#-code quite a lot:
public void UpdateDB(Model.ResultContext db)
{
Model.Planning.Part dbPart = db.Parts.Where(/*someClause*/).FirstOrDefault();
//The awkward part
if (dbPart.Number != Number)
{
dbPart.Number = Number;
}
if (dbPart.NumberShort != NumberShort)
{
dbPart.NumberShort = NumberShort;
}
if (dbPart.Designation != Designation)
{
dbPart.Designation = Designation;
}
}
It is obviously kind of awkward to check every field and wrap it in if != then set
Yes, the check is needed because otherwise the database sees everything as changed columns.
The fields to set are auto-Properties:
public class Part
{
[MaxLength(36), MinLength(1)]
public string Number { get; set; } = null!;
[MaxLength(80)]
public string Designation { get; set; } = null!;
}
and I don't want to write an explicit setter for every field which of course could do the checking before setting.
So what I thought of is some Method ´SetIfChanged´ which is called like this to make the code more readable and less error-prone:
//Options
dbPart.SetIfChanged(dbPart.Number, this.Number);
dbPart.SetIfChanged(dbPart.Number = this.Number);
dbPart.SetIfChanged(Number, this.Number);
I think something like that is possible with expressions or lambdas but to be honest... I'm stuck with the syntax of declaring and calling such a method
Anybody can help me out?
Unfortunately, C# is lacking a number of things to help you with this (e.g. property refs or extension methods on reference objects), but you can use Reflection to help with this. It is likely to be quite slow, however.
With a method that takes a lambda, you can write a set method:
public static void SetIfDifferent<T>(Expression<Func<T>> getterFnE, T newVal) {
var me = (MemberExpression)getterFnE.Body;
var target = me.Expression;
var targetLambda = Expression.Lambda(target);
var prop = me.Member;
var oldVal = getterFnE.Compile().Invoke();
if ((oldVal == null && newVal != null) || !oldVal.Equals(newVal)) {
var obj = targetLambda.Compile().DynamicInvoke();
prop.SetValue(obj, newVal);
}
}
This would be used like:
SetIfDifferent(() => dbPart.Number, Number);
SetIfDifferent(() => dbPart.NumberShort, NumberShort);
SetIfDifferent(() => dbPart.Designation, Designation);
This would be slow because of the need to compile the Expression trees and use DynamicInvoke. One way to speed it up would be to pass in a setter and getter lambda instead, but that leads to as much duplication as your original code.
If you would be willing to pass the object and name of the property instead, you could use:
public static T GetValue<T>(this MemberInfo member, object srcObject) => (T)member.GetValue(srcObject);
public static void SetIfDifferent2<TObj, TField>(this TObj obj, string fieldName, TField newVal) {
var prop = typeof(TObj).GetProperty(fieldName);
var oldVal = prop.GetValue<TField>(fieldName);
if ((oldVal == null && newVal != null) || !oldVal.Equals(newVal))
prop.SetValue(obj, newVal);
}
Which you could use like:
dbPart.SetIfDifferent2(nameof(dbPart.Number), Number);
dbPart.SetIfDifferent2(nameof(dbPart.NumberShort), NumberShort);
dbPart.SetIfDifferent2(nameof(dbPart.Designation), Designation);
Unfortunately, it requires repeating dbPart unless you are willing to just put in the field name (e.g. "Number") but that will cause runtime errors if the field changes.
You could also cache the PropertyInfo instead of looking it up with GetProperty, but that is generally pretty fast and caching probably isn't worth it.
Well if you really need checking (lets say at the end you want to know if anything has been changed or not) you can use Reflection and loop through properties. but in your case no check is needed.
take this for instance:
if (dbPart.Number != Number)
{
dbPart.Number = Number;
}
true) if the value is different you are setting the new one
false) means that the new value and the old value are the same, so doesn't hurt to set it again
If you want to know if anything has changed at the end:
bool changed = false;
var type = dbPart.GetType();
foreach(var (PropertyInfo)pi in type.GetProperties()
{
if(pi.GetValue(dbPart) != newValue)
{
changed = true;
pi.SetValue(dbPart, newValue);
}
}
or you can do something like:
bool changed = dbPart.Number != Number || dbPart.Designation != Designation;
dbPart.Number = Number;
dbPart.Designation = Designation;
I have class A which stores ref to object B in BObject variable.
public class A
{
public B BObject;
}
I want to get BObject ( name of variable ) in B class constructor.
Is there any way to do this ?
Purpose of doing it: I want to create ODBCFramework and I want to get Table Name based on Variable Name. ( Like in EntityFramework Context )
Update: I want to handle it in C#5.
You can use C#-6 nameof operator:
var a = new A();
string bName = nameof(a.B);
Note that generally attempting to relay on a run-time name of a property/field for table lookup seems like a bad idea.
There is no way to do what you want.
You cannot find the name of whatever it is that is storing the reference to your object, that information is simply not available.
Basically, this:
var x = new BObject();
// from inside BObject, get the name "x"
is not possible. The fact that you have stored it in a field of another object changes nothing, it simply cannot be done.
You need to have a way to explicitly tell that object which table name it should use.
Can you use the PropertyInfo class?
var a = B.GetInfo().GetProperties();
foreach(PropertyInfo propertyInfo in a)
string name = propertyInfo.Name
#Damien_The_Unbeliever give me some points to solve my problem. And I tried this, and it works.
public class A
{
public B BObject { get; set; }
public A()
{
var BTypeProperties = this.GetType().GetProperties().Where(x => x.PropertyType == typeof(B));
foreach (var prop in BTypeProperties)
{
prop.SetValue(this, new B(prop.Name));
}
}
}
public class B
{
string _propName;
public B(string propertyName)
{
_propName = propertyName;
}
}
Also, to be clear in answer:
#Yuval Itzchakov suggested that in C#6 solution is:
var a = new A();
string bName = nameof(a.B);
I have a Class with the following:
public class TestClass {
string Account1 {get;set;}
string Account2 {get;set;}
string Account3 {get;set;}
}
What I would like is to be able to have a method that is similar to the following:
public TestClass[] GetTestClass(string value, string AccountName)
where i can pass in a value say "John" and the AccountName would be "Account1"
and it will go through a list of TestClass and return an array or list of TestClass objects where there exists a value "John" in the Property "Account1"
Is there a better method of doing this or any thoughts would help.
Note: This is a model based of a SQL Table
You can use the reflection to get what you want, Your method will look like this,
public List<TestClass> GetTestClass(string value, string AccountName)
{
foreach(TestClass test in yourListOfTestClass)
{
if (test.GetType().GetProperty(AccountName).GetValue(test, null).Equals(value))
listToReturn.Add(test);
}
return listToReturn
}
Note - Code is not tested. Might have synatx error.
You can send in a method for accessing the property instead of the property name:
public TestClass[] GetTestClass(string value, Func<TestClass, string> getAccountName) {
return accounts.Where(x => getAccountName(x) == value).ToArray();
}
Usage:
TestClass[] johnsAccounts = GetTestClass("John", a => a.Account1);
Or simply use it directly:
TestClass[] johnsAccounts = accounts.Where(a => a.Account1 == "John").ToArray();
Is there a better method of doing this or any thoughts would help.
You can use List of strings instead of multiple string variables as you are returning array from GetTestClass. It will keep it simple as well.
public class TestClass
{
List<string> Accounts = new List<string>();
}
Now you will only pass the value to method. Using the LinQ will filter out the desired string list using Where.
public List<string> GetTestClass(string value)
{
return Accounts.Where(account => account == value).ToList();
}
Edit The OP want two things AccountName and the Name, this would require to have a account class instead of string.
class Account
{
string AccountName { get; set; }
string Name { get; set; }
}
public class TestClass
{
List<Account> Accounts = new List<Account>();
}
public Account GetTestClass(Account account)
{
return Accounts.Where(account => a.AccountName == account.AccountName && a.Name == account.Name).FirstOrDefault();
}
Yes you can do this.
Take a look into Reflection. This should get you started.
Example:
public TestClass[] GetTestClass(string value, string AccountName)
{
var propertyInfo = typeof(TestClass).GetProperty(AccountName);
var list = new List<TestClass>();
foreach(var tc in [YOUR_OBJECTS])
{
if(propertyInfo.GetValue(tc, null) == value)
{
list.add(tc);
}
}
return list.ToArray();
}
Well, you can achieve that using Reflection. Though, I don't think I would do that this way, since there are other ways to implement what you want differently.
A better way in my opinion would be to create a type-safe enum that would do that. In the members of the type-safe enum, you could specify an action that would run against a 'TestClass' object.
You would be able to call 'GetTestClass' in this way:
var accounts1 = GetTestClass(testClassesCollection, "john", TestProperties.Account1);
var accounts2 = GetTestClass(testClassesCollection, "john", TestProperties.Account2);
var accounts3 = GetTestClass(testClassesCollection, "john", TestProperties.Account3);
Where 'testClassesColleciton' is the collection of 'TestClass' that you have. You could remove this argument if the method is an object member.
The type-safe enum is implemented this way:
public sealed class TestProperties
{
public static readonly TestProperties Account1 = new TestProperties((t, name) => t.Account1 == name);
public static readonly TestProperties Account2 = new TestProperties((t, name) => t.Account2 == name);
public static readonly TestProperties Account3 = new TestProperties((t, name) => t.Account3 == name);
private Func<TestClass, string, bool> _checkFunc;
private TestProperties(Func<TestClass, string, bool> func)
{
_checkFunc = func;
}
public bool IsApplicable(TestClass test, string name)
{
return _checkFunc(test, name);
}
}
Then, you can implement the GetTestClass this way:
public TestClass[] GetTestClass(IEnumerable<TestClass> testClasses, string value, TestProperties property)
{
return testClasses.Where( t => property.IsApplicable(t)).ToArray();
}
I wouldn't use reflection because of it's performance overhead and it's maintenance danger. With a plain simple reflection, we will pass the property's name as a string parameter. What would happen if somebody has changed the property's name in the class? Even with Visual Studio refactoring (ctrl + R, R), the string parameter value will not be updated. Since the code will compile normally, the bug will be discovered only at run-time.
I agree with #Rouby that a plain simple reflection will be faster to develop than the type-safe enum way that I have suggested. Though, personally, I don't think that it will be that much cheaper (development-cost wise) and I also think that the potentially dangerous scenario that I talked about earlier (regarding refactoring) would have a bigger development-cost penalty, specially, when it is a legacy/old code.
So im new at C# and i need to know if what i want to do is possible and how heres what I have,
public static class Sempre
{
public static string Raca = "";
}
// Sempre.Raca - can use like this
Now What I want to do is set a variable like thing = "example", and after this call Sempre but with the variable something like, Sempre.thing, but because it's a variable it would actually be Sempre.example.
Example same use I want in php,
$example = mean;
$_SESSION['name'.$example];
would create $_SESSION [namemean];
You can setup your type with an indexer. http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx. To use the indexer, you are required to have an instance class rather than a static one. If you really need to, you can use the singleton pattern to get "static" behavior.
Here is an example of using an indexer:
public class Sempre
{
private Dictionary<string, string> _values = new Dictionary<string, string>();
public string this[string key]
{
get { return _values[key]; }
set { _values[key] = value; }
}
}
You can use it like this:
Sempre sempre = new Sempre();
sempre["example"] = "my value";
string thing = "example";
Console.WriteLine(sempre[thing]);
Generally speaking you can not do this with objects in C# since the code is precompiled prior to runtime.
If you are specifically looking for an implementation of http session state, like you have in the PHP code example then this could be done. Session State is exposed at System.Web.SessionState.HttpSessionState and can be accessed via concatenated strings like in your example like this.
String example = "mean";
Session["name" + example] = 'bar';
//Session["namemean"] is now set to value of 'bar'
If you're only looking to do string substitution, you can also do something like this:
public class StringConstants
{
public static string YES = "yes";
public static string NO = "no";
}
then elsewhere
public void printmessage(bool value)
{
if (value)
{
Console.writeline (string.Format "I Say {0}", StringConstants.YES);
}
else
{
Console.writeline (string.Format "I Say {0}", StringConstants.NO);
}
}
Documentation on string.Format for insertions and compositions is here
i want to do a class constructor that takes a dicionary as parameter and initialize all the class variables that are listed as key in the dictionary, after of course a type conversion:
public class User
{
public int id;
public string username;
public string password;
public string email;
public int mana_fire;
public int mana_water;
public int mana_earth;
public int mana_life;
public int mana_death;
public User ()
{
}
public User(Dictionary<string,string> dataArray){
FieldInfo[] classVariablesInfoList = typeof(User).GetFields();
for(int i = 0; i < classVariablesInfoList.Length; i++)
{
if(dataArray.ContainsKey(classVariablesInfoList[i].Name)){
//missing code here :)
//need something like classVariable= dataArray["classVariablesInfolist[i].name"]; ?
}
}
}
}
but i can't find out how to do this!
Can you please help? :)
You can use the SetValue frunction from reflection:
FieldInfo f = classVariablesInfoList[i];
if (f.ReflectedType == typeof(int))
{
f.SetValue(this, Convert.ToInt32(dataArray[f.Name]));
}
else
{
f.SetValue(this, dataArray[classVariablesInfoList[i].Name]);
}
But it is a really uncommon way to do this with a dictionary. You should considder accessing the fields directly or add parameters to the constructor for any field. And fields should never be public - use properties instead.
The following will work if Convert.ChangeType() is able to handle the conversion. There are a lot of problems waiting to occur, for example handling numbers or dates where the string representation depends on the locale. I would really suggest to use usual typed constructor parameters or standard (de)serialization mechanism if possible. Or at least use a dictionary containing objects instead of strings to get rid of the conversion, again if possible.
public User(Dictionary<String, String> data)
{
var fields = typeof(User).GetFields();
foreach (field in fields)
{
if (data.ContainsKey(field.Name))
{
var value = Convert.ChangeType(data[field.Name], field.MemberType);
field.SetValue(this, value);
}
}
}
I would like to separate your problem into two parts.
1. Applying conversion
The FieldInfo type present a FieldType property that is the actual type of the field, using this Type we can use the non-generic ChangeType method of System.Convert, this method will be able convert some types to others. Luckily it support String to Int.
Usage:
Convert.ChangeType(OLD_VALUE, TARGET_TYPE);
2. Setting the field
The field info class has a SetValue method (FieldInfo.SetValue), it has two parameters, the first one is the current (ie. this) instance (or the instance you wish to change). the second is the new value you wish to set.
Putting it all together
[...]
var fieldInfo = classVariablesInfoList[i];
var name = fieldInfo.Name;
var targetType = fieldInfo.Type;
var value = Convert.ChangeType(dataArray[name], targetType);
classVariablesInfoList[i].SetValue(this, value);