2015-09-02 62 views
1

在我们的项目中,我们有多个所谓的Projections。这些Projection-classes都有一个带有get的字符串属性,称为ProjectionTypeName。这ProjectionTypeName应该是唯一的所有投影。反射:通过类类型的Property-GetValue(因此没有实例化的对象)

今天我遇到了这样的问题,其中两个投影有相同的ProjectionTypeName,这造成了很多麻烦。所以我决定做一个UnitTest来防止这种情况再次发生。

我用下面的方法来获取所有Types所有Projections

// The excluded abstract projections 
private static readonly List<Type> AbstractProjections = new List<Type> 
{ 
    typeof(AbstractRavenDbChangesProjection), 
    typeof(AbstractRavenDbTimelineProjection) 
}; 

private static IEnumerable<Type> GetAllProjectionTypes() 
{ 
    return typeof(AbstractRavenDbChangesProjection) 
     .Assembly 
     .GetTypes() 
     .Where(x => typeof(AbstractRavenDbChangesProjection).IsAssignableFrom(x) && !x.IsInterface) 
     .Except(AbstractProjections) 
     .ToList(); 
} 

然后我做了实际测试:

[Test] 
public void TestNoDuplicated() 
{ 
    var noDuplicatedList = new Dictionary<Type, string>(); 
    var projectionTypes = GetAllProjectionTypes(); 
    foreach (var type in projectionTypes) 
    { 
     // TODO: Get projectionTypeName-value by Projection's Type 
     var projectionTypeName = ??; 

     Assert.IsFalse(noDuplicatedList.ContainsValue(projectionTypeName), 
      "{0} has been found as ProjectionTypeName in two different Projections: {1} & {2}", 
       projectionTypeName, 
       noDuplicatedList.First(x => x.Value == projectionTypeName).Key, 
       type); 
      noDuplicatedList.Add(type, projectionTypeName); 
    } 
} 

我四处张望了一下,甚至试图a piece of code by @JohnSkeet,即使他陈述(和我引述):

请不要这样做。永远。这很可怕。它应该被践踏, 切成小块,放在火上,然后再切断。有趣的是, 不是吗? ;)

但是它看起来像自2012年以来(当答案发布时)已被更改和.NET现在给你一个错误,当你尝试这种反射:"System.Security.VerificationException : Operation could destabilize the runtime."

所以,我的问题。如何获取字符串属性ProjectionTypeName的值,当我只有该类的Type供我处理(而不是实际的实例化对象)时。

如果我想有一个实例化对象我将能够做这样的事情:

myInstantiatedObject.GetType().GetProperty("ProjectionTypeName") 
    .GetValue(myInstantiatedObject, null); 

回答

1

好吧,我找到了答案,让约翰飞碟双向的代码工作的基础上this answer

我的问题是这条线:

var dynamicMethod = new DynamicMethod("TempUglyPropertyGetMethod", typeof(string), 
    Type.EmptyTypes, Assembly.GetExecutingAssembly().ManifestModule); 

其失踪Assembly.GetExecutingAssembly().ManifestModule作为额外的参数。这是我的工作测试代码:

// https://stackoverflow.com/questions/11162652/c-sharp-get-property-value-without-creating-instance/11162876#11162876 
var method = type.GetProperty("ProjectionTypeName").GetGetMethod(); 
var dynamicMethod = new DynamicMethod("TempUglyPropertyGetMethod", typeof(string), 
    Type.EmptyTypes, Assembly.GetExecutingAssembly().ManifestModule); 
var generator = dynamicMethod.GetILGenerator(); 
generator.Emit(OpCodes.Ldnull); 
generator.Emit(OpCodes.Call, method); 
generator.Emit(OpCodes.Ret); 
var tempUglyPropertyGetMethod = (Func<string>)dynamicMethod.CreateDelegate(typeof(Func<string>)); 
var projectionTypeName = tempUglyPropertyGetMethod(); 

再一次,不要在实际项目中使用它。我只用它来做这个测试用例。再次引用@JohnSkeet:

请不要这样做。 永远。这很可怕。它应该被践踏 ,切成小块,放在火上,然后再切断。虽然是乐趣 ,不是吗? ;)

+0

为什么不使用Jons答案底部的简单代表解决方案? – usr

+0

@usr因为我不能做他的第二个代码的这一部分:'Func

+1

使用MakeGenericType创建委托类型并使用DynamicInvoke调用它。为您节省了晦涩难懂的IL一代。 – usr

2

那么使用自定义属性呢?

[AttributeUsage(System.AttributeTargets.Class)] 
public sealed class ProjectionTypeNameAttribute : Attribute 
{ 
    private string m_Name; 

    public string Name 
    { 
    get { return m_Name; } 
    } 

    public ProjectionTypeNameAttribute(string name) 
    { 
    m_Name = name; 
    } 
} 

[ProjectionTypeNameAttribute(ProjectionWhatever.PROJECTION_NAME)] 
public class ProjectionWhatever 
{ 
    public const string PROJECTION_NAME = "Whatever"; 

    // if you want to have property as well 
    public string ProjectionTypeName 
    { 
    get 
    { 
     return PROJECTION_NAME; 
    } 
    } 
} 

// query if type has attribute 
var type = typeof(ProjectionWhatever); 
var attributes = type.GetCustomAttributes(typeof(ProjectionTypeNameAttribute), false); 
if (attributes != null && attributes.Length > 0) 
{ 
    var attribute = (ProjectionTypeNameAttribute)attributes[0]; 
    // use attribute.Name 
}  
+0

嗯,看起来很有前途,更干净。我现在很忙,所以我没有时间去尝试它,但是现在需要我注意的其他更高优先级的问题现在我的问题已经被上面的UnitTest修复了。不过,当我有更多时间时,我会将它放在我的TODO列表中。 –

相关问题