Get the object instance which contains a specified property - c#

This question will probably take a while to explain, and I'll need to provide background...
This is just something I'm playing about with and isn't for production, but at the moment I have some code which looks like this:
var myDataModel = new DataModel();
myDataModel.PropertyChanged += myDataModel_PropertyChanged;
myDataModel.ChangeProperty(t => t.TestValue, 2);
So, rather than using myDataModel.TestValue = 2 directly, I'm using a ChangeProperty extension method so that I can handle all of the change events and do anything I want to in one place. My extension method looks like this (and yes, I know it's hacky):
public static class NotifyPropertyChangedExtensions
{
public static void ChangeProperty<T, U>(
this T instance,
Expression<Func<T, U>> propertyToChange,
U newValue)
{
var member = propertyToChange.Body as MemberExpression;
if (member != null)
{
if (!propertyToChange.Compile().Invoke(instance).Equals(newValue))
{
var setProperty = instance.GetType().GetProperty(
member.Member.Name,
BindingFlags.SetProperty |
BindingFlags.Public |
BindingFlags.Instance);
if (setProperty != null)
{
// actually set the property
setProperty.SetValue(instance, newValue, null);
// raise the property changed event
if (typeof(INotifyPropertyChanged).IsAssignableFrom(
typeof(T)))
{
var delegatesToCall =
instance.GetType().GetField("PropertyChanged",
BindingFlags.Instance |
BindingFlags.NonPublic)
.GetValue(instance) as MulticastDelegate;
if (delegatesToCall != null)
{
var eventArgs = new PropertyChangedEventArgs(
setProperty.Name);
foreach (var #delegate in
delegatesToCall.GetInvocationList())
{
#delegate.Method.Invoke(
#delegate.Target,
new object[] { instance, eventArgs });
}
}
}
}
}
}
else
{
throw new ArgumentException(
string.Format(
"Cannot determine the property to change {0}",
propertyToChange));
}
}
}
With this architecture, my data model is quite clean:
public class DataModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int TestValue { get; set; }
}
That is, I can use auto-properties, and don't need to worry about raising events etc.
Now, what I actually want to do is something closer to this:
var dataModel = new DataModel();
myDataModel.PropertyChanged += myDataModel_PropertyChanged;
myDataModel.TestValue.Set(2); // this is what I want...
So, I'm thinking I'll basically need an extension method - but I can only see how to send the property itself (the TestValue in this case), and the new value. So then I wondered if it's possible, given a property, to find out the instance of the class it belongs to?

Don't do this. It breaks encapsulation.
No. In myDataModel.TestValue.Set(2); the extension method will always be called on the value returned by the property. There is no way to get the class, instance or property that returned the value.
You could do something like this:
var t = new DataModel();
((Expression<Func<int>>)(() => t.Foo)).Set(100);
with
static class Extensions
{
public static void Set<T>(this Expression<Func<T>> expression, T value)
{ ... }
}
but this is ugly, almost unreadable, unclear, inefficient, and error prone.
You're looking for Aspect Oriented Programming (AOP).
Have a look at PostSharp or LinFu.
There is no really clean solution to implementing INotifyPropertyChanged yet. If typing all the property setters is too much work or too error prone, I'd generate them with a T4 template.

Related

Generically get the name of the current class?

When logging data, I want a generic reference to the containing class. That way, if the code is moved elsewhere, the class name will change accordingly. (Otherwise, if the code moves to nameof(Class2), it will still be logged incorrectly as nameof(Class1)). For example:
class Class_Name {
ICommand Command_Name =>
new RelayCommand(() =>
{
// An loggable event occurs
// Is there a smart and uncomplicated way of doing this generically?
var provenance = $"{nameof(Class_Name)}.{nameof(Command_Name)}";
// The event of whatever kind gets logged
});
}
// OR
void Method_Name() {
var provenance = $"{nameof(Class_Name)}.{nameof(Method_Name)}";
}
}
Using a generic nameof(this), where this should refer to the class itself, causes a compilation error: CS8081: Expression does not have a name. Using this.GetType() causes the same problem.
Not really understanding why the this keyword does not refer to the containing class in this context. Is there a way to refer to the current class generically?
If you combine the suggestion in the comments (this.GetType().Name) with a [CallerMemberName] attribute via a helper method, you can accomplish what you're looking for in a reusable fashion.
public class Class_Name
{
public void Method_Name()
{
var provenance = CreateProvenance();
Console.WriteLine(provenance);
}
private string CreateProvenance([CallerMemberName] string methodName = "")
{
return $"{this.GetType().Name}.{methodName}";
}
}
This outputs "Class_Name.Method_Name".
You can even turn this into a handy extension method that allows you to call it from any method.
public class Class_Name
{
public void Method_Name()
{
var provenance = this.CreateProvenance();
Console.WriteLine(provenance);
}
}
public static class ProvenanceExtensions
{
public static string CreateProvenance(this object context,
[CallerMemberName] string methodName = "")
{
return $"{context.GetType().Name}.{methodName}";
}
}
As Jeppe Stig Nielsen pointed out, you may not want the inheriting runtime type to be used, which is what context.GetType().Name will return. If you want to get the compile-time type instead, you can use generics.
public static class ProvenanceExtensions
{
public static string CreateProvenance<T>(this T context,
[CallerMemberName] string methodName = "")
{
return $"{typeof(T).Name}.{methodName}";
}
}
By design:
A nameof expression is evaluated at compile time and has no effect at
run time.
To access the type dynamically, in the runtime, you may use the GetType method. Just rememeber not to combine it with the nameof.
class Class_Name {
void Method_Name() {
// An event occurs
// Is there a smart and uncomplicated way of doing this generically?
var provenance = $"{this.GetType().Name}.{MethodBase.GetCurrentMethod().Name}";
// The event of whatever kind gets logged
}
}

How to make NHibernate 5 ConventionModelMapper map ICollection as Set by default

I have some existing models written and I was trying to map them with NHibernate (version 5). As it happened, they are properties exposed as IEnumerable<T> with backing fields declared as ICollection<T>, like so:
public class Encounter
{
public virtual String Title { get; protected set; } = null!;
public virtual IEnumerable<Conversation> Conversations => _conversations.AsEnumerable();
private ICollection<Conversation> _conversations = new HashSet<Conversation>();
protected Encounter() { }
// ...
}
I'm trying to use ConventionModelMapper as much as possible, and so far so good, but I found it doesn't seem to know what to do with ICollection<T>, and I get TransientObjectExceptions when it tries to persist objects in those collections. If I change them to ISet<T>s, it maps by convention just fine... but it seems like this should be something I should be able to easily tell the ConventionModelMapper to do, since I have a number of these ICollection<T> members.
I'm not finding an easy way... This is part of the way there, but overrides the existing IsSet() logic, so I'd have to add back in conditions for ISet members at least, and it doesn't work for my IEnumerable<T> properties with backing fields (which NH is clearly able to deal with because it correctly picks up ISet backing fields)...
var mapper = new ConventionModelMapper();
mapper.IsSet((memberInfo, b1) =>
{
var memberType = memberInfo.GetPropertyOrFieldType();
if (memberType.IsGenericType)
{
return memberType.GetGenericInterfaceTypeDefinitions().Contains(typeof(ICollection<>));
}
return false;
});
Seems like this should be simpler, am I missing an event hook?
After some further experimentation and research, I came up with this, that works... but it seems overwrought and not very DRY. Before I refactor this, someone please tell me there's a better way?
// All this just to get NHibernate to map ICollection as "Set" by default?
mapper.IsSet((memberInfo, b1) =>
{
IEnumerable<Type> typeDefs;
Type memberType = memberInfo.GetPropertyOrFieldType();
if (memberType.IsGenericType)
{
typeDefs = memberType.GetGenericInterfaceTypeDefinitions();
if(typeDefs.Contains(typeof(ISet<>)) || typeDefs.Contains(typeof(ICollection<>)))
{
return true;
}
}
const BindingFlags defaultBinding = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly;
var fieldInfo = (from ps in PropertyToField.DefaultStrategies.Values
let fi = memberInfo.DeclaringType.GetField(ps.GetFieldName(memberInfo.Name), defaultBinding)
where fi != null
select fi).FirstOrDefault();
if (fieldInfo != null)
{
memberType = fieldInfo.GetPropertyOrFieldType();
if (memberType.IsGenericType)
{
typeDefs = memberType.GetGenericInterfaceTypeDefinitions();
if (typeDefs.Contains(typeof(ISet<>)) || typeDefs.Contains(typeof(ICollection<>)))
return true;
}
}
return false;
});
This is near-copypasta and uses code from NHibernate and its included PropertyToField utility class. I mostly understand it, but it's just... verbose... seems like there should be a better option. Again, am I missing a simple hook somewhere?

How to get Getter backing field from PropertyInfo?

I have a class as follows:
class Foo : PropertyChangedBase {
private int _property;
public int Property {
get { return _property; }
set { OnAssignPropertyChanged("Property", () => _property, value); }
}
PropertyChangedBase implements INotifyPropertyChanged with the following methods:
protected void OnAssignmentPropertyChanged<T>(string propertyName, Expression<Func<T>> fieldExpression, T value)
{
var get = fieldExpression.Compile();
if (get().Equals(value))
{
return;
}
// invoke set property method
SetProperty(fieldExpression, value);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void SetProperty<T>(Expression<Func<T>> fieldExpression, T value)
{
if (fieldExpression == null)
{
throw new ArgumentNullException(nameof(fieldExpression));
}
var memberExpression = fieldExpression.Body as MemberExpression;
if (memberExpression == null)
{
throw new ArgumentException("fieldExpression");
}
var field = memberExpression.Member as FieldInfo;
if (field == null)
{
throw new ArgumentException("fieldExpression");
}
field.SetValue(this, value);
}
I would prefer to call:
OnAssignPropertyChanged(() => Property, value);
The only way this will work is if I can get the backing field for the property getter and then pass that to SetProperty. Is it possible to get the FieldInfo or target member from the property get method?
As a general answer, yes you can do, at least under controlled conditions. But the only case you should do this is when you are absoluty sure what you are doing and only with limited support, because there will be cases you can not handle.
Have a look at the answer here: Find all property references using reflection. The target is a bit different but the approach is similar for finding field references. Since the answer there already includes the necessary code i will just outline the way to go:
All metadata items in .Net are referenced by tokens. To get tokens used inside a method you have to parse the MethodBody (by skipping all the things you wont inspect) and then resolve the found tokens in their module. Remember to use the BitConverter when reading the tokens from the stream to resolve them.
But now to the down side; the only time you can really safely use this to find the backing fields of a properties getter, is when you find a simple get method, with a well defined opcode sequence like Ldfld, Ret or something like that. Maybe you can define a few patterns that the C# compiler will emit for simple and autoimplemented properties. If you find anything different there is no other way as to resign and throw an exception, because the getter could contain any code.
Like always with reflection, only use whitelist approaches, check for the conditions you expect and throw exeptions in any other case or you will run into a NullReferenceException sooner or later.
If this is worth the trouble is for you to decide, but in general you could do this since .Net 2.0 and do not even need a fancy external lib.
No, in general case you can't. Just compare two classes:
public class Test {
private int _propA;
private int _propB;
public int PropA {get { return _propA; }}
public int PropB {get { return _propB; }}
}
public class TestSwapped {
private int _propA;
private int _propB;
// please, notice swapped backing fields
public int PropA {get { return _propB; }}
public int PropB {get { return _propA; }}
}
you'll get identical PropertyInfo[] and FieldInfo[] arrays but different backing fields
In pursuing this for a different question, here is an extension method for the simple cases - an autogenerated backing field, or a get that just returns a backing field:
public static class MethodInfoExt {
const int IL_ldarg0 = 0x02;
const int IL_ldfld = 0x7B;
public static FieldInfo FieldInfoFromGetAccessor(this MethodInfo getAccessorMI) {
var body = getAccessorMI.GetMethodBody().GetILAsByteArray();
if (body[0] == IL_ldarg0 && body[1] == IL_ldfld) {
var fieldToken = BitConverter.ToInt32(body, 2);
return getAccessorMI.DeclaringType.Module.ResolveField(fieldToken);
}
else
return default;
}
}
You can not. Property can have no backing fields or sets of backing fields.
Even set property can have no backing fields at all.
public Int32 Prop
{
set { Debug.WriteLine(value.ToString()); }
get { return 1; }
}
What are you expecting to get in FieldInfo?
Property is just a syntax sugar for a pair of set/get methods, or mutators. Being a method allows them to contain as much code as needed, including being just empty and, of course, there is no requirement to have a backing field from compiler perspective.

Changing the PropertyGrid's Description and Category attributes at runtiome

I'm working on a business application that use the PropertyGrid. My project leader want me to localize the texts in the PropertyGrid at runtime. Hurray!!! irony
I have tried many days to localize the PropertyGrid. But I have trouble changing the attributes Description and Category at runtime. Changing the DisplayName works fine.
I have made a simple example to reproduce the issue: Create a Windows Form application and from the ToolBox add a PropertyGrid and a Button with default settings.
Here is the class I would like to display in the PropertyGrid:
class Person
{
int age;
public Person()
{
age = 10;
}
[Description("Person's age"), DisplayName("Age"), Category("Fact")]
public int Age
{
get { return age; }
}
}
In the Form's constructor; I create the Person object and display it in the PropertyGrid.
public Form1()
{
InitializeComponent();
propertyGrid1.SelectedObject = new Person();
}
The button is used to change the DisplayName, Description and Category attributes at runtime.
private void button1_Click(object sender, EventArgs e)
{
SetDisplayName();
SetDescription();
SetCategory();
propertyGrid1.SelectedObject = propertyGrid1.SelectedObject; // Reset the PropertyGrid
}
The SetDisplayName() method works fine and actually changes the DisplayName of the property in runtime!
private void SetDisplayName()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person)["Age"];
DisplayNameAttribute attribute = descriptor.Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute;
FieldInfo field = attribute.GetType().GetField("_displayName", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(attribute, "The age");
}
SetDescription() and SetCategory() methods are almost identical to the SetDisplayName() method, except for some type changes and strings to access the private member of each attributes.
private void SetDescription()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person)["Age"];
DescriptionAttribute attribute = descriptor.Attributes[typeof(DescriptionAttribute)] as DescriptionAttribute;
FieldInfo field = attribute.GetType().GetField("description", BindingFlags.NonPublic |BindingFlags.Instance);
field.SetValue(attribute, "Age of the person");
}
private void SetCategory()
{
Person person = propertyGrid1.SelectedObject as Person;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(person)["Age"];
CategoryAttribute attribute = descriptor.Attributes[typeof(CategoryAttribute)] as CategoryAttribute;
FieldInfo[] fields = attribute.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo field = attribute.GetType().GetField("categoryValue", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(attribute, "Info");
}
Both SetDescription() and SetCategory() methods compile and run but don't effext the ProperytGrid. After the last line of each method you can use the IntelliSense to see that the the Attribute object (DescriptionAttribute and CategoryAttribute) has a member that has changed.
After running these three methods and resetting the PropertyGrid (see button1 click method); the PropertyGrid has only changed the DisplayName attribute. The Description and the Category attributes are unchanged.
I would really like some help to solve this issue. Please any suggestion or solutions?
Note 1:
I don't want any responses saying that this is impossible and the attributes can only be set at design time. That is not true! This article from CodeProject.com show an example how to localize the PropertyGrid and to change the attributes in runtime. Unfortunately I have problem scoping the example for those parts I need to solve this issue.
Note 2:
I would like to avoid using resouce files. This is due to the localization is located in different language files. Each file contain a bunch of indices, each with a string value. All indices and string values are loaded into Dictionary object. To access a string the index is used to access it. I most, unfortunately, use this solution.
Best regards,
/Mc_Topaz
Here is a good article for Globalized-property-grid
You can give many resource file for the Person ,than will the propery-grid will localize.
Here is three step:
inherit from GlobalizedObject
Give the resource file for Person with the same name (eg.Person.zh-cn.resx)
change the thread's cuture which you want to display.
You can try ,Good Luck!
What you could do is reuse the DynamicTypeDescriptor class described in my answer to this question here on SO: PropertyGrid Browsable not found for entity framework created property, how to find it?
like this:
public Form1()
{
InitializeComponent();
Person p = new Person();
DynamicTypeDescriptor dt = new DynamicTypeDescriptor(typeof(Person));
propertyGrid1.SelectedObject = dt.FromComponent(p);
}
private void button1_Click(object sender, EventArgs e)
{
DynamicTypeDescriptor dt = (DynamicTypeDescriptor)propertyGrid1.SelectedObject;
DynamicTypeDescriptor.DynamicProperty dtp = (DynamicTypeDescriptor.DynamicProperty)dt.Properties["Age"];
dtp.SetDisplayName("The age");
dtp.SetDescription("Age of the person");
dtp.SetCategory("Info");
propertyGrid1.Refresh();
}
xudong125 answer solves the issue! I managed to work around the resource files solution by using a static source instead. It's to complicated to explain...
But creating classes implementing ICustomTypeDescriptor and PropertyDescriptor is the way to go.
The key was to override DisplayName, Description and Category methods in the sub class of the PropertyDescriptor class. In these overriden methods I pointed to a public static source and managed to get the strings I wanted.
/Mc_Topaz
I have a different reason to change the property description and found a fairly crude but much simpler solution for just correcting the description that is shown in the grid. Advantage for me was that the class of the object shown in the property grid required much less change.
My situation was the following: I have two boolean properties A and B, where B only can be used if A is set. If A is False, I want to make B read-only and set its description to something like "This property can only be used if you set 'A' to True". In the object code I set the Description attribute of B to this message, similar to how Mc_Topaz does that.
To only set the description that is shown for the selected property to its correct current value, I use the following SelectedGridItemChanged event handler for my PropertyGrid named pgConfig:
private void pgConfig_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
{
GridItem giSelected = e.NewSelection;
if ((giSelected != null) && (giSelected.PropertyDescriptor != null))
{
string sDescription = GetCurrentPropertyDescription(giSelected.PropertyDescriptor.Name);
if ((sDescription != null) && (sDescription != giSelected.PropertyDescriptor.Description))
{
MethodInfo miSetStatusBox = pgConfig.GetType().GetMethod("SetStatusBox", BindingFlags.NonPublic | BindingFlags.Instance);
if (miSetStatusBox != null)
miSetStatusBox.Invoke(pgConfig, new object[] { giSelected.PropertyDescriptor.DisplayName, sDescription });
}
}
}
In the code sample, GetCurrentPropertyDescription is a private function that retrieves the current property description of the object being shown in the property grid (m_da.Config in my case):
private string GetCurrentPropertyDescription(string sPropertyName)
{
PropertyDescriptor oPropDescriptor = TypeDescriptor.GetProperties(m_da.Config.GetType())[sPropertyName];
if (oPropDescriptor != null)
{
DescriptionAttribute oDescriptionAttr = (DescriptionAttribute)oPropDescriptor.Attributes[typeof(DescriptionAttribute)];
if (oDescriptionAttr != null)
return oDescriptionAttr.Description;
}
return null;
}
My solution is less suitable than huoxudong125's if you want full globalization, but if you just want dynamic descriptions for some of your properties without changing the inheritance of the object being shown, it is an option.
Disadvantage of my method is that the underlying cached PropertyDescriptor objects of the grid are never updated, so SetStatusBox will always be called twice if a property with changed description is selected, which is inefficient.
The solution of huoxudong125 is one possible solution. I'd like to offer another one (but without talking about how to change the culture stuff at runtime - you can google that yourself ;) ). For myself I started with using localized subclasses for DisplayName, Description and Category.
As we know, DisplayName does update to current culter when PropertyGrid is updated, but Description and Category do not. I think the reason for this is on reflection level, when PropertyGrid requests category and description. As you can see, those values are cached on first read but displayName is not. To tackle that I developed two solution (where the first one is weird and I do not understand why that works myself..). Both revolve around an extra TypeDescriptionProvider
The base
First goes the custom TypeDescriptionProvider, which can be bound to any class via attribute:
internal class UpdateableGlobalizationDescriptionProvider<TTargetType> : TypeDescriptionProvider
{
private static TypeDescriptionProvider defaultTypeProvider = TypeDescriptor.GetProvider(typeof(TTargetType));
public UpdateableGlobalizationDescriptionProvider() : base(defaultTypeProvider) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
var result = base.GetTypeDescriptor(objectType, instance);
return new ForcedGlobalizationTypeDescriptor(result);
}
}
First solution
This one revolves around "just get it done"... Add a CustomTypeDescriptor implementation, which wraps original PropertyDescriptor instances with the custom ones:
internal class ForcedGlobalizationTypeDescriptor : CustomTypeDescriptor
{
readonly ICustomTypeDescriptor inner;
public ForcedGlobalizationTypeDescriptor(ICustomTypeDescriptor typeDescriptor) : base(typeDescriptor)
{
inner = typeDescriptor;
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
// First solution
var result = base.GetProperties(attributes);
var transformed = result.OfType<PropertyDescriptor>().Select(d => new ForcedPropertyDescriptor(d)).ToArray();
return new PropertyDescriptorCollection(transformed);
}
}
and outer the PropertyDesciptor simply returns values from the wrapped PropertyDescriptor. The simplest implementation for PropertyDescriptor I found - tell me, if there is a shorter one, please.
internal class ForcedPropertyDescriptor : PropertyDescriptor
{
private PropertyDescriptor innerDescriptor;
public ForcedPropertyDescriptor(PropertyDescriptor descriptor) : base(descriptor)
{
innerDescriptor = descriptor;
}
// important:
public override string Category => base.Category;
public override string Description => base.Description;
public override Type ComponentType => innerDescriptor.ComponentType;
public override bool IsReadOnly => innerDescriptor.IsReadOnly;
public override Type PropertyType => innerDescriptor.PropertyType;
public override bool CanResetValue(object component) => innerDescriptor.CanResetValue(component);
public override object GetValue(object component) => innerDescriptor.GetValue(component);
public override void ResetValue(object component) => innerDescriptor.ResetValue(component);
public override void SetValue(object component, object value) => innerDescriptor.SetValue(component, value);
public override bool ShouldSerializeValue(object component) => innerDescriptor.ShouldSerializeValue(component);
}
I think it works, because for every read of category or description there is a new ForcedPropertyDescriptor, which has not cached the value, yet. At the same time this is a drawback: for like every request on the Category or Description propery a new instance of ForcedPropertyDescriptor is created, while Microsoft's implementation seems to cache craeated PropertyDescriptors somewehere.
Second solution
To avoid that instance creation every time, I simply stored every seen ProperyDescriptor created by ForcedGlobalizationTypeDescriptor in a set. And as soon as a localization change appears, that set gets called to reset it's items cached values:
internal class DescriptorReset
{
public static DescriptorReset Default { get; } = new DescriptorReset();
private HashSet<MemberDescriptor> descriptors = new HashSet<MemberDescriptor>();
public void Add(MemberDescriptor descriptor)
{
descriptors.Add(descriptor);
}
private void RunUpdate()
{
if (descriptors.Count == 0)
return;
FieldInfo category, description;
category = typeof(MemberDescriptor).GetField(nameof(category), BindingFlags.NonPublic | BindingFlags.Instance);
description = typeof(MemberDescriptor).GetField(nameof(description), BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var descriptor in descriptors)
{
category.SetValue(descriptor, null);
description.SetValue(descriptor, null);
}
}
}
The RunUpdate method uses Reflection to reset inner fields to null, so on the next call to corresponding properties the localized values are read again.
All you need now is some magic to call RunUpdate at the right moment. For myself I have a class in my core solution, which provides a method to set a new CultureInfo. When called, it sets the default ui culture and the default culture to the new CultureInfo and raises two events: the first one is to update all internal logic and the second one is for everything based on internal logic, like the GUI.
And since I do not know where and how long Microsoft's PropertyDescriptors are stored, I created a HashSet with WeakReference (based on WeakHashTable) to store corresponding references.
Usage
Simply append the DescriptionProvider class to your class shown in PropertyGrid:
[LocalizedDescription(nameof(MyClass), typeof(MyTextResource))]
[TypeDescriptionProvider(typeof(ForcedGlobalizationTypeDescriptor<MyClass>))]
class MyClass
{
// ...
The way your LocalizedDescription works, depends on you...

How to create a reference to a value-field

Is there a way in C# to create a field which is a reference to another field which is a value type?
class myClass
{
bool b1;
public void method1(ref bool b)
{
b1 = b;
}
}
I want b1 to reference the value of b, just as b references the value of the original argument, so that changes to b1 will affect the original argument.
EDIT:
What I’m trying to achieve is a myCheckBox class which automatically updates a field. See: How do I change a value argument from within an event handler?
Sure! Take a look at Eric's answer to this question:
Setting a ref to a member field in C#
As others have pointed out, you cannot store a reference to a variable
in a field in C#, or indeed, any CLR language.
Of course you can capture a reference to a class instance that
contains a variable easily enough
Well... there is a very contort way :) of course.
That is, using reflection!
You cannot get the address of a field, but we can use reflection.
Reflection is slower than accessing directly a field, i warn you.
And really, accessing private fields of other classes is a really bad practice!
Is however useful sometime for some dirty hacks when you don't have control of code written by other people.
Here the example, but i keep saying, it is not a good practice, is here only for curiosity and for educational purposes!
Fine another way to access your field, using properties or using a class that modify your properties.
// Our FieldReference class that internally uses reflection to get or set a field value.
public class FieldReference<T>
{
private object ownerObject;
private FieldInfo fieldInfo;
public FieldReference(object ownerObject, string fieldName)
{
this.ownerObject = ownerObject;
this.fieldInfo = ownerObject.GetType().GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
}
public FieldReference(object ownerObject, FieldInfo fieldInfo)
{
this.ownerObject = ownerObject;
this.fieldInfo = fieldInfo;
}
public T Value
{
get { return (T)this.fieldInfo.GetValue(this.ownerObject); }
set { this.fieldInfo.SetValue(this.ownerObject, value); }
}
}
// Our dummy class
public class MyClass
{
// Our field we want to expose.
private int myField;
public MyClass(int value)
{
this.myField = value;
}
// Just a function we use to print the content of myField.
public override string ToString()
{
return this.myField.ToString();
}
}
class Program
{
public static void Main()
{
// We create our class.
MyClass mc = new MyClass(5);
// We print field value, should be 5 :)
Console.WriteLine(mc.ToString());
// We create our field reference
FieldReference<int> fieldref = new FieldReference<int>(mc, "myField");
// We set the value using field reference.
// Note, we accessed a private field :)
fieldref.Value = 100;
// Now we print the value, should be 100!
Console.WriteLine(mc.ToString());
Console.ReadLine();
}
}
Looks like something that is better solved using delegates/events.
Instead of trying to do the impossible (force value types to behave as reference types), use an event and fire it whenever this value is changed.
Subscribe to this event from the caller/s and you are good to go.
Not knowing what you would want this for you could use a delegate for this, it does sound like a code smell though:
class myClass
{
Action<bool> modify;
public void method1(Action<bool> modify)
{
this.modify = modify;
}
public void ModifyIt()
{
modify(false);
}
}
bool b1 = true; //b1 is true
var m = new myClass();
m.method1(val => { b1 = val; });
m.ModifyIt(); //b1 is false now

Categories