I have two classes with property getters only
public class A
{
public A(string name)
{
Name = name;
}
public string Name { get; }
public string Value { get; set;}
public string Data { get; set;}
}
public class B
{
public B(string name)
{
Name = name;
}
public string Name { get; }
public string Value { get; set;}
}
They are different in shape but share some of the same property names and types.
How can I copy values when they only have getters?
This is a typical scenario when I send an object as a constructor parameter to extract values from in the new object. Then I need to copy values one by one. This can produce lots of code and is hard to maintain.
Can this be made simpler? Is there a way to use reflection to copy objects when the target only has getter properties?
It is possible to create a helper function that copies two objects where the target object only has property getters.
Try this;
public static void CopyItem<U, T>(U source, T target)
{
// Need a way to rename the backing-field name to the property Name ("<A>k__BackingField" => "A")
Func<string, string> renameBackingField = key => new string(key.Skip(1).Take(key.IndexOf('>') - 1).ToArray());
// Get public source properties (change BindingFlags if you need to copy private memebers as well)
var sourceProperties = source.GetType().GetProperties().ToDictionary(item => item.Name);
// Get "missing" property setter's backing field
var targetFields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetField).ToDictionary(item => renameBackingField(item.Name));
// Copy properties where target name matches the source property name
foreach(var sourceProperty in sourceProperties)
{
if (targetFields.ContainsKey(sourceProperty.Key) == false)
continue; // No match. skip
var sourceValue = sourceProperty.Value.GetValue(source);
targetFields[sourceProperty.Key].SetValue(target, sourceValue);
}
}
This is a generic description. You will probably want to check the property and field data types as well before you copy the value to prevent exceptions. Both name and data type should match.
Use CopyItem to copy matching source properties in a constructor;
public class SomeClass
{
public SomeClass(SomeSourceClass source)
{
Helper.CopyItem(source, this);
}
}
Related
I am wondering if something like this is possible. I am looking to create a method that can be called instead of List.Add. The method would check any/all string properties and make sure they don't exceed their specific given max lengths, and if so truncate to proper size. I would ideally like it to be generic so that it will not only work for ObjectA, but also ObjectB, ObjectC, etc.
I am open to any and all suggestions. I know it seems like a weird thing to do, but I have a lot of different objects I am working with and potentially millions of those object instances in totality across all my lists. I mainly just need a way to ensure that any objects with properties exceeding their max string limit are truncated and logged via the Worker class in a timely way. Thanks!
public class ObjectA {
public Guid aID {get; set;}
[MaxLength(128)]
public string aName {get; set;}
[MaxLegnth(30)]
public string aType {get; set;}
}
--
public class Worker {
private void Work() {
List<ObjectA> listOfA = new List<ObjectA>();
listOfA.CustomAddMethod(new ObjectA(new Guid, "Something", "Unknown"));
}
// ??????
private CustomAddMethod(T object) {
foreach property {
if (isStringProperty && isGreaterThanMaxLength) {
// truncate to proper size
// log for truncation message
}
// Then add to list
}
}
}
You can create an extension method.
Here is a code snip. You can improve the performance by implementing a cache, for example, using a dictionary to store the properties and the MaxLengthAttribute based on object's type.
public static class ListExtensions
{
public static void CustomAdd<T>(this List<T> list, T item, Action<string> logger = null)
{
var propertyInfos = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(propertyInfo => propertyInfo.PropertyType == typeof(string))
.ToList();
foreach (var propInfo in propertyInfos)
{
var maxLengthAttr = propInfo
.GetCustomAttributes(typeof(MaxLengthAttribute))
.Cast<MaxLengthAttribute>()
.FirstOrDefault();
if (maxLengthAttr is null)
continue;
var currentString = (string)propInfo.GetValue(item);
if (!maxLengthAttr.IsValid(currentString))
{
var newValue = currentString.Substring(0, maxLengthAttr.Length);
logger?.Invoke(
$"Resolving error: {maxLengthAttr.FormatErrorMessage(propInfo.Name)}\n" +
$"Old Value: {currentString}\n" +
$"New Value: {newValue}"
);
propInfo.SetValue(item, newValue);
}
}
list.Add(item);
}
}
Example of usage (code removed for brevity):
public class Person
{
[MaxLength(4)]
public string Name { get; set; }
}
...
var personList = new List<Person>();
personList.CustomAdd(
new Person {Name = "John Doe"},
message => Debug.WriteLine(message)
);
...
As result the Jhon Doe string will be trimmed to Jhon
I have a class that contains multiple string fields. Whenever an object of this class is instantiated, I'd like those fields to be automatically assigned with the same specific default value (something like "Undefined"). The reason is:
If I have to serialize the object before all fields are populated with real data, I want those fields to display as this default value rather than being null or string.Empty.
String fields may be added/removed from this class as the project progresses. I'd like to not have to touch the constructor every time that occurs.
Is there any way to do this other than explicitly assigning the default value to each of the string fields one by one in the class constructor?
In C# 6.0 and above, you can use Auto-Property Initializer:
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-6#auto-property-initializers
Basically:
public string Property { get; set; } = "UNDEFINED";
You would have to use reflection. Something like this
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(string)) property.setValue(obj, "UNDEFINED");
}
First of all: I don't see how it could be best practice to do what you want.
If you want something like this to show up in your code:
public string Property { get; set; } = "UNDEFINED";
You should probably look into creating custom snippets that simply write exactly that. e.g. https://msdn.microsoft.com/en-us/library/ms165394.aspx
If you don't want that, you could use reflection to find all fields (e.g. strings) in the constructor and set them.
C# Reflection - Get field values from a simple class
FieldInfo[] fields = data.GetType().GetFields(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
Setting a property by reflection with a string value
Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
Well, why not have an extension method like
public static class MyClass
{
public static string GetDefault(this str, string defaultVal)
{
return string.IsNullOrEmpty(str) ? defaultVal : str;
}
}
For a type
public class SomeClass
{
public string str = string.Empty;
}
You can call
SomeClass s = new SomeClass();
s.str.GetDefault("UNDEFINED");
You can initialize values to fields directly instead of in the constructor.
private string myStringVariable = "UNDEFINED";
Perhaps you should reconsider the structure of your program though if it permits many fields to be initialized to undefined.
Maybe I am misunderstanding this but why not do word for word what you described in the question in your constructor?
public class Weee
{
public string name { get; set; }
public int order { get; set; }
public string whatever { get; set; }
public Weee()
{
foreach(var p in typeof(Weee).GetProperties().Where(a => a.PropertyType == typeof(string)))
{
p.SetValue(this, "wut");
}
}
}
You can create a property initializer and have a base class use it. Your classes can then inherit from the base and have their properties automatically initialized:
public class PropertyInitializer
{
public void Initialize<T>(object obj, T value)
{
PropertyInfo[] properties = obj.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(T))
{
property.SetValue(obj, value);
}
}
}
}
public class InitializedBase
{
protected InitializedBase()
{
var initializer = new PropertyInitializer();
//Initialize all strings
initializer.Initialize<string>(this, "Juan");
//Initialize all integers
initializer.Initialize<int>(this, 31);
}
}
//Sample class to illustrate
public class AutoInitializedClass : InitializedBase
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return string.Format("My name is {0} and I am {1} years old", Name, Age);
}
}
Sample usage:
AutoInitializedClass sample = new AutoInitializedClass();
Console.WriteLine(sample);
Console output:
My name is Juan and I am 31 years old
Notice the base class is using the PropertyInitializer class to initialize fields. This is a simplified example. You can expand it as it fits you (it may not work out of the box with all types).
I personally don't recommend this. It's called a constructor for a reason but you asked a question and I provided an answer.
Here is a simple class from which you can inherit that does exactly what you want:
Example usage:
public class MyClass : DefaultedObject<string>
{
public string MyStringField;
protected override string Default => "UNDEFINED";
}
var myClass = new MyClass();
// myClass.MyStringField == "UNDEFINED"
Implementation:
public abstract class DefaultedObject<T>
{
protected DefaultedObject()
{
T defaultValue = Default;
FieldInfo[] fields = GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach(FieldInfo field in fields) {
if(field.FieldType == typeof(T)) {
field.SetValue(this, defaultValue);
}
}
}
protected abstract T Default { get; }
}
I appreciate all the feedback to this question. Here's what ended up working. First, for any string attributes in the class that I wanted to receive an automatic default value, I established as a property:
public string attribute1 {get; set;}
public string attribute2 {get; set;}
And so on. Then, in the class constructor, I included the following loop which iterates through each property of type string:
foreach(PropertyInfo property in GetType().GetProperties())
{
if (property.PropertyType == typeof(string))
property.SetValue(this, "UNDEFINED"));
}
This produced the desired outcome for me.
Short description:
I need to sanitize some text before it is saved in the database.
We have a project that contain all the models that are used in our MVC application. There's a Save(T record) method that saves the entity to the database. What I was asked to do is to sanitize every property of type string of the incoming object, before saving it to the database. However, there's a problem: How do I sanitize the properties of the property of a custom data type, from the incoming entity?
Long description:
Let's say I have a class of type Address and a class of type Person that has a property of type Address:
public class Address
{
public string StreetName { get; set; }
public string City { get; set; }
public int StreetNumber { get; set; }
}
public class Person
{
public string PersonName { get; set; }
public Address HomeAddress { get; set; }
}
Before I was using this generic method to retrieve the properties of type string:
public static void SanitizeObject<TEntity>(TEntity record)
{
var props =
record.GetType().GetProperties()
.Where(x => x.CanRead && x.CanWrite)
.Where(x => x.PropertyType == typeof (string));
foreach (var property in props)
{
string value = Convert.ToString(record.GetPropertyValue(property.Name));
if (!string.IsNullOrEmpty(value))
{
value = Sanitize(value);
record.SetPropertyValue(property.Name, value);
}
}
}
In that case I will sanitize only the Person.PersonName, however, I need to sanitize also the Address.StreetName and the Address.City
Is there a way to write this lambda expression to get the childrens' properties of type string also? How should I perform this to get all the properties of type string so i could sanitize them?
It seems like you need a recursive method.
Pseudo-code:
public void SanitizeObject(object some)
{
// We get properties which are of reference types because we don't want to iterate value types (do you want to sanitize an integer...?)
foreach (PropertyInfo property in some.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(prop => !prop.PropertyType.IsValueType)
{
if (property.PropertyType == typeof (string))
{
// Do stuff to sanitize the string
}
else
{
// Get properties declared in the concrete class (skip inherited members)
var properties = property.DeclaringType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
// Does the property type has properties?
if (properties != null && properties.Length > 0)
{
// This gets the custom object and starts a recursion to sanitize its string properties if the object have string properties, of course...
SanitizeObject(property.GetValue(some));
}
}
}
}
Note that I removed the generic parameter. I believe that, as you'll use reflection to get properties to sanitize, there's no advantage on using generics at all. And using this approach you'll be able to sanitize any objects instead of only supporting entity types.
I want to get the value of a field of an object by using a string as variable name.
I tried to do this with reflection:
myobject.GetType().GetProperty("Propertyname").GetValue(myobject, null);
This works perfectly but now I want to get the value of "sub-properties":
public class TestClass1
{
public string Name { get; set; }
public TestClass2 SubProperty = new TestClass2();
}
public class TestClass2
{
public string Address { get; set; }
}
Here I want to get the value Address from a object of TestClass1.
You already did everything you need to do, you just have to do it twice:
TestClass1 myobject = ...;
// get SubProperty from TestClass1
TestClass2 subproperty = (TestClass2) myobject.GetType()
.GetProperty("SubProperty")
.GetValue(myobject, null);
// get Address from TestClass2
string address = (string) subproperty.GetType()
.GetProperty("Address")
.GetValue(subproperty, null);
Your SubProperty member is actually a Field and not a Property, that is why you can not access it by using the GetProperty(string) method. In your current scenario, you should use the following class to first get the SubProperty field, and then the Address property.
This class will allow you to specify the return type of your property by closing the type T with the appropriate type. Then you will simply need to add to the first parameter the object whose members you are extracting. The second parameter is the name of the field you are extracting while the third parameter is the name of the property whose value you are trying to get.
class SubMember<T>
{
public T Value { get; set; }
public SubMember(object source, string field, string property)
{
var fieldValue = source.GetType()
.GetField(field)
.GetValue(source);
Value = (T)fieldValue.GetType()
.GetProperty(property)
.GetValue(fieldValue, null);
}
}
In order to get the desired value in your context, simply execute the following lines of code.
class Program
{
static void Main()
{
var t1 = new TestClass1();
Console.WriteLine(new SubMember<string>(t1, "SubProperty", "Address").Value);
}
}
This will give you the value contained in the Address property. Just make sure you first add a value to the said property.
But should you actually want to change the field of your class into a property, then you should make the following change to the original SubMember class.
class SubMemberModified<T>
{
public T Value { get; set; }
public SubMemberModified(object source, string property1, string property2)
{
var propertyValue = source.GetType()
.GetProperty(property1)
.GetValue(source, null);
Value = (T)propertyValue.GetType()
.GetProperty(property2)
.GetValue(propertyValue, null);
}
}
This class will now allow you to extract the property from your initial class, and get the value from the second property, which is extracted from the first property.
try
myobject.GetType().GetProperty("SubProperty").GetValue(myobject, null)
.GetType().GetProperty("Address")
.GetValue(myobject.GetType().GetProperty("SubProperty").GetValue(myobject, null), null);
I would like to automatically generate SQL statements from a class instance. The method should look like Update(object[] Properties, object PrimaryKeyProperty). The method is part of an instance (class, base method - generic for any child). Array of properties is an array of class properties, that will be used in update statement. Property names are equal to table field names.
The problem is that I can't get property names.
Is there any option to get a property name inside class instance?
sample:
public class MyClass {
public int iMyProperty { get; set; }
public string cMyProperty2 { get; set; }
{
main() {
MyClass _main = new MyClass();
_main.iMyProperty.*PropertyName* // should return string "iMyProperty"
{
I am aware of PropertyInfo, but I don't know hot to get the ID of a property from GetProperties() array.
Any suggestion?
Just wrote an implementation of this for a presentation on lambdas for our usergroup last Tuesday.
You can do
MembersOf<Animal>.GetName(x => x.Status)
Or
var a = new Animal()
a.MemberName(x => x.Status)
the code:
public static class MembersOf<T> {
public static string GetName<R>(Expression<Func<T,R>> expr) {
var node = expr.Body as MemberExpression;
if (object.ReferenceEquals(null, node))
throw new InvalidOperationException("Expression must be of member access");
return node.Member.Name;
}
}
Link to the presentation and code samples.
Also in SVN (more likely to be updated): http://gim-projects.googlecode.com/svn/presentations/CantDanceTheLambda
I found a perfect solution in This Post
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
And then for the usage :
var propertyName = GetPropertyName(
() => myObject.AProperty); // returns "AProperty"
Works like a charm
You can do something like this:
Type t = someInstance.getType();
foreach (MemberInfo mi in t.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
Console.WriteLine(mi.Name);
}
}
to get all the property names for instance's type.
You can get the name (I assume that's what you meant by ID) of a property using PropertyInfo.Name. Just loop through the PropertyInfo[] returned from typeof(className).GetProperties()
foreach (PropertyInfo info in typeof(MyClass).GetProperties())
{
string name = info.Name;
// use name here
}
Since you already have an explicit handle to the specific property you want, you know the name - can you just type it?
Not 100% sure if this will get you what you're looking for, this will fetch all properties with [Column] attribute inside your class:
In the datacontext I have:
public ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity>( )
{
return this.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(typeof(TEntity)).DataMembers;
}
Fetching the table column-names that are properties inside the class:
MyDataContext db = GetDataContext();
var allColumnPropertyNames = db.ColumnNames<Animal>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
Let's say (from the first sample, method update of a class MyClass):
public class MyClass {
public int iMyStatusProperty { get; set; }
public int iMyKey { get; set; }
public int UpdateStatusProperty(int iValue){
this.iMyStatusProperty = iValue;
return _Update( new[iMyStatusProperty ], iMyKey); // this should generate SQL: "UPDATE MyClass set iMyStatusProperty = {iMyStatusProperty} where iMyKey = {iMyKey}"
}
{iMyStatusProperty} and {iMyKey} are property values of a class instance.
So, the problem is how to get property name (reflection) from a property without using names of properties as strings (to avoid field name typos).