LoginSignup
2
3

More than 5 years have passed since last update.

リフレクションで、半分だけ private のプロパティに値をセットする

Posted at

次のようなクラスがあり、スタティックの Current プロパティに対して値をセットしたくなりました。ところが、setprivate になっているため、通常ではセットできないのでリフレクションを使うことにしました。

    public class SomeClass
    {
        private static AsyncLocal<string> current = new AsyncLocal<string>();
        public static string Current
        {
            get { return current.Value; }
            private set { current.Value = value; }
        }

        public SomeClass(string message)
        {
            Current = message;
        }
    }

static メソッド、プロパティに関する値の取得とセット

C# の場合、これは極めて簡単で、Type を取得したのち、値をセットしたり、ゲットしたり、メソッドを実行したりできます。


            var property = typeof(SomeClass).GetProperty("Current", BindingFlags.Static | BindingFlags.Public);            
            property.SetValue(null, "World");

            var method = typeof(SomeClass).GetMethod("Message", BindingFlags.Static | BindingFlags.NonPublic);
            method.Invoke(null, new object[] {"Tsuyoshi"});

GetPropertyGetMethod 等、アクセスしたい対象に関数メソッドが容易されています。 ポイントとしては、BindingsFlags で、static であること、Public/NonPublic であることを指定して、メソッドやプロパティを検索することです。私が少しだけはまったポイントとしては、Current プロパティは、public ですが、set メソッドが private のため、当初は、NonPublic を指定していました。そうすると、property が null で返ってきてしまいました。この公開レベルは、最初の定義の部分が該当するようですので、それにあわせて、public にすると、property が取得できるようになりました。

尚、リフレクションを使うと、private のほうである、set のプロパティも問題なくセットできました。

BindingFlags の実装は下記のようになっており、Or をとることで、ビットを立てることで、権限を表現しています。

 public enum BindingFlags
  {
    /// <summary>Specifies that reflection should create an instance of the specified type. Calls the constructor that matches the given arguments. The supplied member name is ignored. If the type of lookup is not specified, (Instance | Public) will apply. It is not possible to call a type initializer.
    /// This flag is passed to an InvokeMember method to invoke a constructor.</summary>
    /// <returns></returns>
    CreateInstance = 512, // 0x00000200
    /// <summary>Specifies that only members declared at the level of the supplied type&amp;#39;s hierarchy should be considered. Inherited members are not considered.</summary>
    /// <returns></returns>
    DeclaredOnly = 2,
    /// <summary>Specifies that no binding flags are defined.</summary>
    /// <returns></returns>
    Default = 0,
    DoNotWrapExceptions = 33554432, // 0x02000000
    /// <summary>Specifies that types of the supplied arguments must exactly match the types of the corresponding formal parameters. Reflection throws an exception if the caller supplies a non-null Binder object, since that implies that the caller is supplying BindToXXX implementations that will pick the appropriate method.</summary>
    /// <returns></returns>
    ExactBinding = 65536, // 0x00010000
    /// <summary>Specifies that public and protected static members up the hierarchy should be returned. Private static members in inherited classes are not returned. Static members include fields, methods, events, and properties. Nested types are not returned.</summary>
    /// <returns></returns>
    FlattenHierarchy = 64, // 0x00000040
    /// <summary>Specifies that the value of the specified field should be returned.
    /// This flag is passed to an InvokeMember method to get a field value.</summary>
    /// <returns></returns>
    GetField = 1024, // 0x00000400
    /// <summary>Specifies that the value of the specified property should be returned.
    /// This flag is passed to an InvokeMember method to invoke a property getter.</summary>
    /// <returns></returns>
    GetProperty = 4096, // 0x00001000
    /// <summary>Specifies that the case of the member name should not be considered when binding.</summary>
    /// <returns></returns>
    IgnoreCase = 1,
    /// <summary>Used in COM interop to specify that the return value of the member can be ignored.</summary>
    /// <returns></returns>
    IgnoreReturn = 16777216, // 0x01000000
    /// <summary>Specifies that instance members are to be included in the search.</summary>
    /// <returns></returns>
    Instance = 4,
    /// <summary>Specifies that a method is to be invoked. This must not be a constructor or a type initializer.
    /// This flag is passed to an InvokeMember method to invoke a method.</summary>
    /// <returns></returns>
    InvokeMethod = 256, // 0x00000100
    /// <summary>Specifies that non-public members are to be included in the search.</summary>
    /// <returns></returns>
    NonPublic = 32, // 0x00000020
    /// <summary>Returns the set of members whose parameter count matches the number of supplied arguments. This binding flag is used for methods with parameters that have default values and methods with variable arguments (varargs). This flag should only be used with <see cref="M:System.Type.InvokeMember(System.String,System.Reflection.BindingFlags,System.Reflection.Binder,System.Object,System.Object[],System.Reflection.ParameterModifier[],System.Globalization.CultureInfo,System.String[])"></see>.</summary>
    /// <returns></returns>
    OptionalParamBinding = 262144, // 0x00040000
    /// <summary>Specifies that public members are to be included in the search.</summary>
    /// <returns></returns>
    Public = 16, // 0x00000010
    /// <summary>Specifies that the PROPPUT member on a COM object should be invoked. PROPPUT specifies a property-setting function that uses a value. Use PutDispProperty if a property has both PROPPUT and PROPPUTREF and you need to distinguish which one is called.</summary>
    /// <returns></returns>
    PutDispProperty = 16384, // 0x00004000
    /// <summary>Specifies that the PROPPUTREF member on a COM object should be invoked. PROPPUTREF specifies a property-setting function that uses a reference instead of a value. Use PutRefDispProperty if a property has both PROPPUT and PROPPUTREF and you need to distinguish which one is called.</summary>
    /// <returns></returns>
    PutRefDispProperty = 32768, // 0x00008000
    /// <summary>Specifies that the value of the specified field should be set.
    /// This flag is passed to an InvokeMember method to set a field value.</summary>
    /// <returns></returns>
    SetField = 2048, // 0x00000800
    /// <summary>Specifies that the value of the specified property should be set. For COM properties, specifying this binding flag is equivalent to specifying PutDispProperty and PutRefDispProperty.
    /// This flag is passed to an InvokeMember method to invoke a property setter.</summary>
    /// <returns></returns>
    SetProperty = 8192, // 0x00002000
    /// <summary>Specifies that static members are to be included in the search.</summary>
    /// <returns></returns>
    Static = 8,
    /// <summary>Not implemented.</summary>
    /// <returns></returns>
    SuppressChangeType = 131072, // 0x00020000
  }

コード全体

using System;
using System.Reflection;
using System.Threading;

namespace ReflectionSpike
{

    public class SomeClass
    {
        private static AsyncLocal<string> current = new AsyncLocal<string>();
        public static string Current
        {
            get { return current.Value; }
            private set { current.Value = value; }
        }

        public SomeClass(string message)
        {
            Current = message;
        }

        private static void Message(string name)
        {
            Console.WriteLine($"Hello World, {name}");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {

            var some = new SomeClass("hello");
            var property = typeof(SomeClass).GetProperty("Current", BindingFlags.Static | BindingFlags.Public);            
            property.SetValue(null, "World");

            var method = typeof(SomeClass).GetMethod("Message", BindingFlags.Static | BindingFlags.NonPublic);
            method.Invoke(null, new object[] {"Tsuyoshi"});

            Console.WriteLine(SomeClass.Current);
            Console.ReadLine();


        }
    }
}

まとめ

値のセットや取得のリフレクションは極めて簡単でした。ただし、公式ページの CustomBindings の仕様はもう少し理解したいと思っています。これは Azure Functions の内部で使われているものなので、理解したいなと思います。

2
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3