在使用它们之前动态查找使用过的属性

时间:2014-01-04 00:03:13

标签: c# reflection

我正在寻找我用于动态表单应用程序的模式的优化。

我有一个带有方法的存储库类:

public Entity Find(string objectId, List<string> includedProperties);

这返回一个Entity对象,只包含“includedProperties”中指定的字段,因为在这种情况下为所有目的构建整个对象是不必要的开销(某些实体有数百个属性)。

使用此存储库的示例域代码通常如下所示:

var includedProperties = new List<string> {
    "FirstChildName" , 
    "FirstChildDob",
    "SecondChildName",
    "SecondChildDob"
}

然后我获取一个对象:

var person = repository.Find("123",includedProperties);

然后我使用GetProperty(string propertyName)方法使用属性:

var firstChildDob = person.GetProperty("FirstChildDob").AsDateTime();
...etc

这一切都很好,并且非常适合应用程序的动态设计。但是,我发现在获取对象之前,我总是需要单独声明一个“used”属性列表,这令人恼火。

所以,我的问题是,通过反思或其他一些聪明,我可以通过查看稍后在代码中使用“GetProperty”方法传递哪些参数来简化“包含属性”的构建吗?

使用上面的例子,我想使用像这样(或类似)的帮助器来构建列表:

var includedProperties = HelperObject.GetFieldsUsedInCurrentCodeFile();

这会以某种方式拾取传递给“GetProperty()”方法的字符串常量,从而节省了显式声明的需要。欢迎任何建议!

2 个答案:

答案 0 :(得分:2)

我实际上有一段相似的问题;我当时能想到的最好的方法是定义一个包含我想在方法中使用的属性名称的枚举。

使用这种方法,您可以通过在枚举中循环来构建包含属性的列表。

这种方法与字符串相比有几个好处:

  1. 任何属性拼写问题或属性名称更改都在一个位置进行。

  2. 如果您使用的是Resharper等工具,则可以确定枚举中何时使用未使用的“属性”。

  3. 例如:

        private enum PersonMethodProperties
        {
            FirstChildName,
            FirstChildDob,
            SecondChildName,
            SecondChildDob
        }
    
        private void PersonMethod()
        {
            var includedProperties = GetIncludePropertiesFromEnum(typeof(PersonMethodProperties));
    
            var person = repository.Find("123", includedProperties);
    
            var firstChildDob = person.GetProperty(PersonMethodProperties.FirstChildDob.ToString()).AsDateTime();
        }
    
        private List<string> GetIncludePropertiesFromEnum(Type propertiesEnumType)
        {
            var includedProperties = new List<string>();
    
            foreach (var name in Enum.GetNames(propertiesEnumType))
            {
                includedProperties.Add(name);
            }
    
            return includedProperties;
        }
    

答案 1 :(得分:2)

NitriqNDepend等代码分析工具很遗憾无法帮助您,因为在当前版本中,它们不会捕获方法的参数名称和值。

但您可以使用Roslyn创建工具来分析您的解决方案并生成包含列表的类,其中包含已使用属性作为预构建事件。解决方案分析代码,查找对classNamemethodName的所有调用并返回其常量参数(在您的案例中为文本):

static IEnumerable<string> GetMethodCallParametersValues(string solutionName, 
                                                         string className, 
                                                         string methodName)
{
    var workspace = Workspace.LoadSolution(solutionName);
    var solution = workspace.CurrentSolution;

    var createCommandList = new List<ISymbol>();
    var @class = solution.Projects.Select(s => s.GetCompilation()
                                                .GetTypeByMetadataName(className))
                                  .FirstOrDefault();
    var method = @class.GetMembers(methodName)
                        .AsList()
                        .Where(s => s.Kind == CommonSymbolKind.Method)
                        .FirstOrDefault();
    var locations = method.FindReferences(solution)
                          .SelectMany(r => r.Locations);

    List<string> result = new List<string>();
    foreach (var location in locations)
    {
        var model = location.Document.GetSemanticModel();
        var token = location.Location
                            .SourceTree
                            .GetRoot()
                            .FindToken(location.Location.SourceSpan.Start);
        var invocation = token.Parent.FirstAncestorOrSelf<InvocationExpressionSyntax>();
        var arguments = invocation.ArgumentList.Arguments;
        result.AddRange(arguments.Select(a => model.GetConstantValue(a.Expression).Value.ToString()));
    }
    return result.Distinct();
}

和代码用法:

var includedProperties = GetMethodCallParametersValues(@"c:\path\to\your.sln",
                                                       "SomeNamespace.SomeClass",
                                                       "GetProperty");

注意:由于Roslyn在解析解决方案文件时存在小错误,您可能需要添加.sln来评论#文件中的以下行,以便它们看起来:< / p>

# VisualStudioVersion = 12.0.21005.1
# MinimumVisualStudioVersion = 10.0.40219.1