24

假设我有一个名为 Test 的类,它有一个名为 Title 的属性和一个自定义属性:

public class Test
{
    [DatabaseField("title")]
    public string Title { get; set; }
}

以及一个名为 DbField 的扩展方法。我想知道在 c# 中是否可以从对象实例中获取自定义属性。

Test t = new Test();
string fieldName = t.Title.DbField();
//fieldName will equal "title", the same name passed into the attribute above

这可以做到吗?

4

6 回答 6

30

这是一种方法。扩展方法有效,但并不那么容易。我创建一个表达式,然后检索自定义属性。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ConsoleApplication1
{
    public class DatabaseFieldAttribute : Attribute
    {
        public string Name { get; set; }

        public DatabaseFieldAttribute(string name)
        {
            this.Name = name;
        }
    }

    public static class MyClassExtensions
    {
        public static string DbField<T>(this T obj, Expression<Func<T, string>> value)
        {
            var memberExpression = value.Body as MemberExpression;
            var attr = memberExpression.Member.GetCustomAttributes(typeof(DatabaseFieldAttribute), true);
            return ((DatabaseFieldAttribute)attr[0]).Name;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var p = new Program();
            Console.WriteLine("DbField = '{0}'", p.DbField(v => v.Title));

        }
        [DatabaseField("title")]
        public string Title { get; set; }

    }
}
于 2010-02-24T22:38:59.213 回答
6
namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();

            Console.WriteLine(t.FieldName("Title").FieldName<DatabaseFieldAttribute>());
            Console.WriteLine(t.FieldName("Title").FieldIsPrimaryKey<DatabaseFieldAttribute>());
        }


    }

    public class Test
    {
        [DatabaseField("titlezzz", true)]
        public string Title
        {
            get;
            set;
        }
    }


    public class BaseDatabaseFieldAttribute : Attribute
    {
        private readonly string _name;

        public string Name { get { return _name; } }

        public BaseDatabaseFieldAttribute(string name)
        {
            _name = name;
        }
    }
    public class DatabaseFieldAttribute : BaseDatabaseFieldAttribute
    {
        private readonly bool _isPrimaryKey;

        public bool IsPrimaryKey { get { return _isPrimaryKey; } }

        public DatabaseFieldAttribute(string name, bool isPrimaryKey): base(name)
        {
            _isPrimaryKey = isPrimaryKey;
        }
    }

    public static class Helper
    {

        public static PropertyInfo FieldName(this object obj, string propertyName)
        {
            return obj.GetType().GetProperty(propertyName);
        }

        public static string FieldName<T>(this PropertyInfo property) where T: BaseDatabaseFieldAttribute
        {
            object[] os = property.GetCustomAttributes(typeof(T), false);

            if (os != null && os.Length >= 1)
                return (os[0] as T).Name;
            else
                return "N/A";
        }

        public static bool? FieldIsPrimaryKey<T>(this PropertyInfo property) where T : DatabaseFieldAttribute
        {
            object[] os = property.GetCustomAttributes(typeof(T), false);

            if (os != null && os.Length >= 1)
                return (os[0] as T).IsPrimaryKey;
            else
                return null;
        }
    }


}
于 2010-02-24T21:48:48.373 回答
2

确实是这样,但最终这将是一种迂回的方式,因为您将Type通过调用GetType暴露属性的实例来获取实例,然后再处理它(通常情况下)。

在这种特定情况下,您的扩展方法将无法获取属性信息,因为您传递给它的只是一个字符串。

归根结底,您需要的是可以从中获得PropertyInfo财产的东西。其他答案指的是Type,他们缺乏的是,这不是获取所需属性信息的唯一方法PropertyInfo

您可以通过传递Type带有字符串的实例来做到这一点,大概带有属性名称,因此您可以调用GetProperty.Type

自 C# 3.0 以来执行此操作的另一种方法是使用一种方法,该方法采用 anExpression<T>然后使用 的部分Expression来获取PropertyInfo. 在这种情况下,您将使用字符串Expression<Func<string>>或某物。TResult

拥有 后PropertyInfo,您可以调用GetCustomAttributes它并查找您的属性。

表达式方法的优点是Expression<T>派生自LambdaExpression,您可以调用Compile它,然后在需要时调用以获取实际值。

于 2010-02-24T21:38:43.503 回答
1

正如所指出的,使用原始海报描述的语法是不可能的,因为您无法在扩展方法中获得对 PropertyInfo 的引用。像这样的东西怎么样:

// Extension method
public static string GetDbField(this object obj, string propertyName)
{
    PropertyInfo prop = obj.GetType().GetProperty(propertyName);
    object[] dbFieldAtts = prop.GetCustomAttributes(typeof(DatabaseFieldAttribute), true);

    if (dbFieldAtts != null && dbFieldAtts.Length > 0)
    {
        return ((DatabaseFieldAttribute)dbFieldAtts[0]).Name;
    }

    return "UNDEFINED";
}

然后,您可以像这样简单地获取信息:

Test t = new Test();
string dbField = t.GetDbField("Title");
于 2010-02-24T22:14:14.640 回答
0

不,这是不可能的。这样做的原因是它是值,而不是属性本身,它将被发送到任何将获取此信息的自定义扩展方法中。一旦进入该扩展方法,就没有可靠的方法可以追溯到属性本身。

枚举值可能是可能的,但就 POCO 上的属性而言,它不会起作用。

于 2010-02-24T21:40:42.630 回答
0

为了获取属性值,您需要属性适用的类型。您的扩展方法仅获取一个字符串值(Title 的值),因此您将无法获取该字符串来自的实际实例,因此您无法获取 Title 属性所属的原始类型。这将使您无法从扩展方法中获取属性值。

于 2010-02-24T21:41:32.233 回答