简体   繁体   中英

Reflection: Property-GetValue by Class-Type (so without an instantiated Object)

In our project we have multiple so-called Projections . These Projection -classes all have a string property with a get, called ProjectionTypeName . This ProjectionTypeName should be unique for all Projections.

Today I had the problem where two Projections had the same ProjectionTypeName which caused quite a lot of trouble. So I decided to make a UnitTest to prevent this from happening again.

I use the following method to get all the Types of all the 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();
}

Then I made the actual test:

[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);
    }   
}

I've looked around a bit and even tried a piece of code by @JohnSkeet , even though he states (and I quote):

Please don't do this. Ever. It's ghastly. It should be trampled on, cut up into little bits, set on fire, then cut up again. Fun though, isn't it? ;)

But it looks like this has been changed since 2012 (when the answer was posted) and .NET now gives an error when you try this kind of reflection: "System.Security.VerificationException : Operation could destabilize the runtime." .

So, my question. How do I get the value of the string-property ProjectionTypeName , when I only have the Type of the class to my disposal (and not an actual instantiated object).

If I would had an instantiated object I would be able to do something like this:

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

What about using custom attribute instead ?

[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
}    

Ok, I've found the answer to make John Skeet's code work, based on this answer .

My problem was this line:

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

which was missing Assembly.GetExecutingAssembly().ManifestModule as additional argument. This is my working test code:

// 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();

Once again, don't use this in an actual project. I've only used it for this test case. To once again quote @JohnSkeet:

Please don't do this. Ever. It's ghastly. It should be trampled on, cut up into little bits, set on fire, then cut up again. Fun though, isn't it? ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM