认知属性(Attribute)

此属性(Attribute )而非彼属性(Property)。MSDN描述:属性(Attribute )能够提供功能强大的方法,以将声明信息与 C# 代码(类型、方法、属性等)相关联。一旦属性与程序实体关联,即可在运行时使用名为反射的技术对属性进行查询。属性 (Attribute) 描述如何将数据序列化,指定用于强制安全性的特性,并限制实时 (JIT) 编译器的优化,从而使代码易于调试。属性 (Attribute) 还可以记录文件名或代码作者,或在窗体开发阶段控制控件和成员的可见性等等。

属性具有以下特点:

  • 属性可向程序中添加元数据。元数据是嵌入程序中的信息,如编译器指令或数据描述。
  • 程序可以使用反射检查自己的元数据。请参见使用反射访问属性。
  • 通常使用属性与 COM 交互。

经过对属性的研究我们做一个小示例,用于阐明属性的使用。我们尝试写个方法,它以两个对象为参数,然后将源对象运行时的所有字段值都拷贝给目标对象。按照一般情况,只要是可序列化的类、字段和方法都可以通过反射机制完成上述操作。但是如果遇到[NonSerialized]限定的字段我们将无从下手。面对这种情形我们创建一个方法,不仅可以存取[Serialized]限定的字段,还可以还原那些经[NonSerialized]限定的字段并在运行时重新赋值!

我们先创建一个可序列化的类:

[Serializable]
public class FooClass
{
   public int data1;
  [NonSerialized] public int data2;
   public FooClass(int data1, int data2)
  {
       this.data1 = data1;
       this.data2 = data2;
  }
}

然后在Button1的Click事件中添加如下代码:

private void button1_Click(object sender, EventArgs e)
{
    FooClass destObject = new FooClass(10, 20);
    FooClass sourceObject = new FooClass(10, 30);
    MessageBox.Show("destObject.data2: "+destObject.data2+'\n'+"sourceObject.data2: "+sourceObject.data2);
    RestoreUtils.RestoreNonSerialized(destObject, sourceObject);
    MessageBox.Show("destObject.data2: " + destObject.data2 + '\n' + "sourceObject.data2: " + sourceObject.data2);
}

注意,RestoreUtils.RestoreNonSerialized()方法,

public class RestoreUtils
{
    const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
​
    public static void RestoreNonSerialized(object dest, object source)
    {
        if (dest == null || source == null)
            return;
​
        Type objectType = dest.GetType();
        if (objectType.IsPrimitive | objectType.IsValueType)
            return;
​
        FieldInfo[] dstFieldsInfo = objectType.GetFields(flags);
        FieldInfo[] srcFieldsInfo = source.GetType().GetFields(flags);
​
        for (int fieldIndex = 0; fieldIndex < dstFieldsInfo.Length; fieldIndex++)
        {
            FieldInfo fieldInfo = dstFieldsInfo[fieldIndex];
            if (fieldInfo.IsNotSerialized)
            {
                object newValue = srcFieldsInfo[fieldIndex].GetValue(source);
                fieldInfo.SetValue(dest, newValue);
                continue;
            }
​
            object dstFieldObject = fieldInfo.GetValue(dest);
            object srcFieldObject = srcFieldsInfo[fieldIndex].GetValue(source);
            if (dstFieldObject != null && srcFieldObject != null)
                RestoreNonSerialized(dstFieldObject, srcFieldObject);
        }
​
        IEnumerable dstEnumerable = dest as IEnumerable;
        if (dstEnumerable != null)
        {
            IEnumerable srcEnumerable = source as IEnumerable;
            if (srcEnumerable != null)
            {
                IEnumerator srcEnumerator = srcEnumerable.GetEnumerator();
                foreach (Object dstItem in dstEnumerable)
                {
                    bool next = srcEnumerator.MoveNext();
                    if (!next)
                        break;
                    Object srcItem = srcEnumerator.Current;
​                    RestoreNonSerialized(dstItem, srcItem);
                }
            }
        }
    }
}

我们会发现,FooClass类的data2字段为不可序列化字段,在没有还原不可序列化的字段之前,destObject.data2和sourceObject.data2的值是不同的,为20和30,当我们还原并将sourceObject.data2在运行时的值拷贝给destObject.data2后,两个FooClass类的实例的data2字段就完全一样了。

认知属性(Attribute)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动到顶部