0

Question:

In a Xamarin PCL project, is there any way to verify that a property with a given name exists, for a property that is not public?


Context:

In my ViewModelBase class, the OnPropertyChanged calls a debug-only method. It helps me finding wrong arguments for OnPropertyChanged in case I could not use the CallerMemberName logic and had to pass an explicit value for propertyName:

public abstract class ViewModelBase : INotifyPropertyChanged
{

  // ...

  protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    VerifyPropertyName(propertyName);
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    // More code that is the reason why calling OnPropertyChanged can
    // make sense even for private or protected properties in my code.
  }

  [Conditional("DEBUG"), DebuggerStepThrough]
  private void VerifyPropertyName(string propertyName)
  {
    // This only finds public properties
    if (GetType().GetRuntimeProperty(propertyName) == null) {
      // TODO
      // Check if there is a non-public property
      // If there is, only print a hint
      throw new ArgumentException("Invalid property name: " + propertyName);
    }
  }

  // ...

}

The problem is that GetRuntimeProperty only finds public properties, i. e. calling OnPropertyChanged for a private property triggers that exception.

I would like to weaken the condition so that calling OnPropertyChanged for non-public properties only triggers a Debug.Print, because my ViewModelBase.OnPropertyChanged contains more code so that calling OnPropertyChanged can become beneficial for private or protected properties.

But neither the System.Type object obtained by GetType() nor System.Reflection.TypeInfo obtained through GetType().GetTypeInfo() as mentioned in this SO answer seem to have methods to find anything else but public properties.

Community
  • 1
  • 1
LWChris
  • 3,320
  • 1
  • 22
  • 39

2 Answers2

1

The actual answer, as per Furkan Fidan's comment:

GetRuntimeProperty only finds public properties, so it returns null if the given property is not public.

However you can retrieve a list of all properties (including the non-public ones) by calling the GetRuntimeProperties method. You can then filter the result using LINQ.

private void VerifyPropertyName(string propertyName)
{
  if (GetType().GetRuntimeProperty(propertyName) != null) {
    // The property is public
  } else if (GetType().GetRuntimeProperties().Any(p => p.Name == propertyName)) {
    // The property is not public, but it exists
  } else {
    // The property does not exist
  }
}

See also the remarks of GetRuntimeProperties:

This method returns all properties defined on the specified type, including inherited, non-public, instance, and static properties.

Community
  • 1
  • 1
LWChris
  • 3,320
  • 1
  • 22
  • 39
0

You can get the private fields of a type by passing the non public binding flag to GetFields method. See a quick and dirty example below.

using System;
using System.Reflection;

class MyClass
{
    private int privField;

    public MyClass(int val)
    {
        this.privField = val;
    }

    public void printValueOfPrivate()
    {
        var fields = this.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
        Console.WriteLine("Name: " + fields[0].Name + " Value: " + fields[0].GetValue(this));
    }
}

public class HelloWorld
{
    static public void Main()
    {
        var mc = new MyClass(7);
        mc.printValueOfPrivate();
    }
}

Output:

Name: privField Value: 7

Edit:

See comments for details but basically to achieve what @LWChris wants you need to call GetRuntimeProperties() on GetType() as GetRuntimeProperty() doesn't return private properties.

Furkan Fidan
  • 121
  • 1
  • 4
  • Sorry my bad - I've only tried with mono on the MacOS. Looking a bit deeper, you're right GetFields is not supported on PCL but instead GetRuntimeFields & GetRuntimeProperties need to be used. [MSDN documentation](https://msdn.microsoft.com/en-us/library/system.reflection.runtimereflectionextensions.getruntimeproperties(v=vs.110).aspx) remarks that it should include non-private as well. What's the documentation you are looking? – Furkan Fidan Jan 18 '17 at 13:29
  • The "documentation" is that I get an exception when I run my code that uses `if (GetType().GetRuntimeProperty(propertyName) == null) throw ...`. – LWChris Jan 18 '17 at 13:57
  • And `GetType().GetTypeInfo().GetDeclaredProperty(string propertyName)` has a documentation that is "Returns an object that represents the specified **public** property declared by the current type". – LWChris Jan 18 '17 at 14:02
  • Documentation for GetRuntimeProperty doesn't say anything about it retrieving non-public fields. I know it may not be efficient but for the sake of science can you try calling GetRuntimeProperties and enumerating through them to see if the property you're looking for is there? – Furkan Fidan Jan 18 '17 at 14:02
  • Hah, you're right! `GetType().GetRuntimeProperty(propertyName)` returns null, but `GetType().GetRuntimeProperties()` returns an array that actually contains an element for the property. Edit your answer to contain this information and I'll accept it. – LWChris Jan 18 '17 at 14:47
  • Glad to hear it worked - we both learned something! I'll update the answer for future reference. – Furkan Fidan Jan 18 '17 at 15:20
  • We should adapt the provided code example rather than adding an "Edit"-section that states "forget the above, this is the actual solution". I'll update the answer later this evening and then accept it. – LWChris Jan 18 '17 at 15:36
  • I'm sorry, I tried to improve your answer and give the credit to you by accepting your answer, but my edit was rejected because it obviously changed the whole code. So I had to post the suggested edit as answer myself and accept that, because that's the right way for PCL. – LWChris Feb 09 '17 at 16:53
  • No worries - thanks for taking the time to share it!! – Furkan Fidan Feb 10 '17 at 17:35