I'm trying to find a way to get access to attributes which have been applied at the property level from within an object, but am hitting a brick wall. It appears that the data is stored at the type level, but I need it at the property level.
Here's an example of what I'm trying to make:
public class MyClass
{
[MyAttribute("This is the first")]
MyField First;
[MyAttribute("This is the second")]
MyField Second;
}
public class MyField
{
public string GetAttributeValue()
{
// Note that I do *not* know what property I'm in, but this
// should return 'this is the first' or 'this is the second',
// Depending on the instance of Myfield that we're in.
return this.MyCustomAttribute.value;
}
}
Attributes aren't used that way. Attributes are stored in the class object that contains the attribute, not the member object/field that is declared within the class.
Since you want this to be unique per member, it feels more like this should be member data with MyField, something passed in by the constructor.
If you're hell bent on using an attribute, you could add code in your constructor that used reflection on every member that has an attribute attached to it an attempts to set its instance data to what you want, but you would need to make sure that all your MyField objects are fully constructed.
Alternately you could make your setter look like this:
private MyField _first;
[MyAttribute("This is the first")]
public MyField First {
get { return _first; }
set {
_first = value;
if (_first != null) {
_first.SomeAttribute = GetMyAttributeValue("First");
}
}
}
private string GetMyAttributeValue(string propName)
{
PropertyInfo pi = this.GetType().GetPropertyInfo(propName);
if (pi == null) return null;
Object[] attrs = pi.GetCustomAttributes(typeof(MyAttribute));
MyAttribute attr = attrs.Length > 0 ? attrs[0] as MyAttribute : null;
return attr != null ? attr.Value : null;
}
Related
This is my code where I create a "copy" of one object (Entity) into a custom object.
It copies just properties with the same name in both source and target.
My problem is when an Entity has a navgiaton to another Entity, for this case I added a custom attribute that I add above the property in the custom class.
For example the custom class looks like:
public class CourseModel:BaseDataItemModel
{
public int CourseNumber { get; set; }
public string Name { get; set; }
LecturerModel lecturer;
[PropertySubEntity]
public LecturerModel Lecturer
{
get { return lecturer; }
set { lecturer = value; }
}
public CourseModel()
{
lecturer = new LecturerModel();
}
}
The problem is in targetProp.CopyPropertiesFrom(sourceProp); line, when I try to call extension method again (to copy the nested object) ,because the type is determined on run time, extension method couldn't resolved on compile time.
Maybe I am missing something...
public static void CopyPropertiesFrom(this BaseDataItemModel targetObject, object source)
{
PropertyInfo[] allProporties = source.GetType().GetProperties();
PropertyInfo targetProperty;
foreach (PropertyInfo fromProp in allProporties)
{
targetProperty = targetObject.GetType().GetProperty(fromProp.Name);
if (targetProperty == null) continue;
if (!targetProperty.CanWrite) continue;
//check if property in target class marked with SkipProperty Attribute
if (targetProperty.GetCustomAttributes(typeof(SkipPropertyAttribute), true).Length != 0) continue;
if (targetProperty.GetCustomAttributes(typeof(PropertySubEntity), true).Length != 0)
{
//Type pType = targetProperty.PropertyType;
var targetProp = targetProperty.GetValue(targetObject, null);
var sourceProp = fromProp.GetValue(source, null);
targetProp.CopyPropertiesFrom(sourceProp); // <== PROBLEM HERE
//targetProperty.SetValue(targetObject, sourceEntity, null);
}
else
targetProperty.SetValue(targetObject, fromProp.GetValue(source, null), null);
}
}
You'll have to cast first.
((BaseDataItemModel)targetProp).CopyPropertiesFrom(sourceProp);
Either you need to cast targetProperty to BaseDataItemModel so you can call the extension method on it (edit: as in agent-j's answer), or otherwise you could just forget about that base class. Why does your reflection algorithm need it? It could work on any class, and is directed purely by the attributes on properties.
And if it works on any object, it shouldn't be an extension method.
I have an app with a pretty big configuration.
All configuration sections for each parameter are defined with .Net ConfigurationProperty attributes, that all have a DefaultValue property.
As our product becomes heavily customizable between countries, and even clients in one country, there is a Configurator.exe that enable to edit the big configuration file.
In this Configurator.exe, it would be really cool if I could have access to the many many many DefaultValue properties that has been defined... However, I don't have the single idea of how I could access those properties generated by the attributes.
e.g.:
public class MyCollection : ConfigurationElementCollection
{
public MyCollection ()
{
}
[ConfigurationProperty(MyAttr,IsRequired=false,DefaultValue=WantedValue)]
public MyAttributeType MyAttribute
{
//... property implementation
}
}
What I need is to programmatically access to the value WantedValue, the most generically as possible. (Otherwise I am to manually browse all the ConfigSections defined, collect the DefaultValues for each field, then check my configurator uses these values... )
In fancy it would look like: MyCollection.GetListConfigurationProperty() that would return ConfigurationPropertyAttribute objects on which I could call the Properties : Name, IsRequired, IsKey, IsDefaultCollection, and DefaultValue
Any idea ?
This is the class I happened to achieve which succeed in what I wanted to do:
I feed it with the ConfigSection type, the type of the defualt value of the field I want, and the string value of the field I want.
public class ExtensionConfigurationElement<TConfigSection, UDefaultValue>
where UDefaultValue : new()
where TConfigSection : ConfigurationElement, new()
{
public UDefaultValue GetDefaultValue(string strField)
{
TConfigSection tConfigSection = new TConfigSection();
ConfigurationElement configElement = tConfigSection as ConfigurationElement;
if (configElement == null)
{
// not a config section
System.Diagnostics.Debug.Assert(false);
return default(UDefaultValue);
}
ElementInformation elementInfo = configElement.ElementInformation;
var varTest = elementInfo.Properties;
foreach (var item in varTest)
{
PropertyInformation propertyInformation = item as PropertyInformation;
if (propertyInformation == null || propertyInformation.Name != strField)
{
continue;
}
try
{
UDefaultValue defaultValue = (UDefaultValue) propertyInformation.DefaultValue;
return defaultValue;
}
catch (Exception ex)
{
// default value of the wrong type
System.Diagnostics.Debug.Assert(false);
return default(UDefaultValue);
}
}
return default(UDefaultValue);
}
}
Given a PropertyInfo object, how can I check that the setter of the property is public?
Check what you get back from GetSetMethod:
MethodInfo setMethod = propInfo.GetSetMethod();
if (setMethod == null)
{
// The setter doesn't exist or isn't public.
}
Or, to put a different spin on Richard's answer:
if (propInfo.CanWrite && propInfo.GetSetMethod(/*nonPublic*/ true).IsPublic)
{
// The setter exists and is public.
}
Note that if all you want to do is set a property as long as it has a setter, you don't actually have to care whether the setter is public. You can just use it, public or private:
// This will give you the setter, whatever its accessibility,
// assuming it exists.
MethodInfo setter = propInfo.GetSetMethod(/*nonPublic*/ true);
if (setter != null)
{
// Just be aware that you're kind of being sneaky here.
setter.Invoke(target, new object[] { value });
}
.NET properties are really a wrapping shell around a get and set method.
You can use the GetSetMethod method on the PropertyInfo, returning the MethodInfo referring to the setter. You can do the same thing with GetGetMethod.
These methods will return null if the getter/setter is non-public.
The correct code here is:
bool IsPublic = propertyInfo.GetSetMethod() != null;
public class Program
{
class Foo
{
public string Bar { get; private set; }
}
static void Main(string[] args)
{
var prop = typeof(Foo).GetProperty("Bar");
if (prop != null)
{
// The property exists
var setter = prop.GetSetMethod(true);
if (setter != null)
{
// There's a setter
Console.WriteLine(setter.IsPublic);
}
}
}
}
You need to use the underling method to determine accessibility, using PropertyInfo.GetGetMethod() or PropertyInfo.GetSetMethod().
// Get a PropertyInfo instance...
var info = typeof(string).GetProperty ("Length");
// Then use the get method or the set method to determine accessibility
var isPublic = (info.GetGetMethod(true) ?? info.GetSetMethod(true)).IsPublic;
Note, however, that the getter & setter may have different accessibilities, e.g.:
class Demo {
public string Foo {/* public/* get; protected set; }
}
So you can't assume that the getter and the setter will have the same visibility.
Am creating a custom attribute for my properties and was wondering if anyone know how i could access the the value of the Attribute inside of the get accessor.
public class MyClass
{
[Guid("{2017ECDA-2B1B-45A9-A321-49EA70943F6D}")]
public string MyProperty
{
get { return "value loaded from guid"; }
}
}
Setting aside the wisdom of such a thing...
public string MyProperty
{
get
{
return this.GetType().GetProperty("MyProperty").GetCustomAttributes(typeof(GuidAttribute), true).OfType<GuidAttribute>().First().Value;
}
}
You can retrieve the property and then its custom attributes via reflection, like this:
// Get the property
var property = typeof(MyClass).GetProperty("MyProperty");
// Get the attributes of type “GuidAttribute”
var attributes = property.GetCustomAttributes(typeof(GuidAttribute), true);
// If there is an attribute of that type, return its value
if (attributes.Length > 0)
return ((GuidAttribute) attributes[0]).Value;
// Otherwise, we’re out of luck!
return null;
I have a Variable class and and 3 subclasses: VariableBool, VariableLong and VariableDouble. Each subclass defines only a value member of the type of the suffix.
Now, I need to transfert objects based on these classes over WCF. I have multiple clients registering their variale to a server. Whenever a value changes on one client, it's updated in all other clients.
My question is: is there a way to do:
someVar.Value = anotherVar.Value;
regardless of the type, wihout having to check for type, e.g.:
VariableBool anotherVarBool = anotherVar as VariableBool;
if (anotherVarBool != null) {
(someVar as VariableBool).Value = anotherVar.Value;
}
// check other types...
What am I missing? Is there a patern of some kind? Could I use reflection?
Also, I don't think I can use Generics because of the WCF (I've tried but I could make it work).
Thanks
If you are using mex-generated WCF proxies, then I suspect reflection (or ComponentModel) is indeed the simplest option - something like:
public static void Copy<T>(T source, T destination,
string propertyName) {
PropertyInfo prop = typeof(T).GetProperty(propertyName);
prop.SetValue(destination, prop.GetValue(source, null), null);
}
Or if you want to use it even with the variable types as the base-class:
public static void Copy(object source, object destination,
string propertyName) {
PropertyInfo sourceProp = source.GetType().GetProperty(propertyName);
PropertyInfo destProp = destination.GetType().GetProperty(propertyName);
destProp.SetValue(destination, sourceProp.GetValue(source, null), null);
}
Why don't you to put the Value member in the base class Variable.
In that case,
public void UpdateValue( Variable variable )
{
if( variable != null )
// do something with variable.Value
}
However, if you really want to use inheritance, you need to tell the base class what are the sub types by using KnownType attribute and its method
[DataContract()]
[KnownType( "GetKnownType" )]
public class Variable
{
public object Value;
private static Type[] GetKnownType()
{
// properties
return new []{ typeof(VariableBool),
typeof(VariableLong),
typeof(VariableDouble),};
}
}
[DataContract()]
public class VariableBool : Variable
{
}
[DataContract()]
public class VariableLong : Variable
{
}
[DataContract()]
public class VariableDouble : Variable
{
}