리플렉션을 사용하여 속성 클래스에서 클래스의 속성에 액세스하려고합니다. 가능한가? 예를 들어C# 속성이 대상 클래스에 액세스 할 수 있습니까?
:
직접class MyAttribute : Attribute
{
private void AccessTargetClass()
{
// Do some operations
}
}
[MyAttribute]
class TargetClass
{
}
리플렉션을 사용하여 속성 클래스에서 클래스의 속성에 액세스하려고합니다. 가능한가? 예를 들어C# 속성이 대상 클래스에 액세스 할 수 있습니까?
:
직접class MyAttribute : Attribute
{
private void AccessTargetClass()
{
// Do some operations
}
}
[MyAttribute]
class TargetClass
{
}
하지,하지만 순서 속성이 아무것도 할 수 있도록, 이미 형/회원에 대한 핸들을 가지고 있어야하고 간단하게 전달할 수 있도록, 그 GetCustomAttributes
메서드를 호출에 거기에서 :
class MyAttribute : Attribute
{
public void DoSomethingWithDeclaringType(Type t)
{
}
}
과 같이 그것을 사용 :
이Type classType = typeof(MyClass);
object[] attribs = classType.GetCustomAttributes(typeof(MyAttribute), true);
if(attribs.Length > 0)
{
((MyAttribute)attribs[0]).DoSomethingWithDeclaringType(classType);
}
가 아칸소 e 형식 참조를 전달하기 위해 호출 코드에 의존 할 수없는 경우.
다음은이 문제를 해결하기 위해 해결 한 해결책입니다. 약간의 샷건 접근 방식이지만 작동합니다 ...
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
namespace Ethica.Reflection
{
/// <summary>
/// Helps in discovery of the target of an Attribute
/// </summary>
/// <typeparam name="TAttribute">An Attribute derived type</typeparam>
/// <remarks>
/// The .NET framework does not provide navigation from attributes back to their targets, principally for the reason that
/// in typical usage scenarios for attributes, the attribute is discovered by a routine which already has a reference to a
/// member type.
///
/// There are, however, bona-fide cases where an attribute needs to detect it's target - an example is a localizable sub-class of the
/// DescriptionAttribute. In order for the DescriptionAttribute to return a localized string, it requires a resource key and, ideally,
/// a type reference as the base-key for the ResourceManager. A DescriptionAttribute could not provide this information without
/// a reference to it's target type.
///
/// Note to callers:
///
/// Your Attribute-derived class must implement Equals and GetHashCode, otherwise a run-time exception will occur, since this class
/// creates a dictionary of attributes in order to speed up target lookups.
/// </remarks>
public static class AttributeTargetHelper<TAttribute>
where TAttribute : Attribute
{
/// <summary>
/// Map of attributes and their respective targets
/// </summary>
private static Dictionary<TAttribute, object> targetMap;
/// <summary>
/// List of assemblies that should not be rescanned for types.
/// </summary>
private static List<string> skipAssemblies;
/// <summary>
/// Adds an attribute and it's target to the dictionary
/// </summary>
/// <param name="attribute"></param>
/// <param name="item"></param>
private static void Add(TAttribute attribute, object item)
{
targetMap.Add(attribute, item);
}
/// <summary>
/// Scans an assembly for all instances of the attribute.
/// </summary>
/// <param name="assembly"></param>
private static void ScanAssembly(Assembly assembly)
{
const BindingFlags memberInfoBinding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
if (!skipAssemblies.Contains(assembly.FullName))
{
skipAssemblies.Add(assembly.FullName);
Debug.WriteLine("Loading attribute targets for " + typeof(TAttribute).Name + " from assembly " + assembly.FullName);
foreach (TAttribute attr in assembly.GetCustomAttributes(typeof(TAttribute), false))
Add(attr, assembly);
foreach (Type type in assembly.GetTypes())
{
foreach (TAttribute attr in type.GetCustomAttributes(typeof(TAttribute), false))
Add(attr, type);
foreach (MemberInfo member in type.GetMembers(memberInfoBinding))
{
foreach (TAttribute attr in member.GetCustomAttributes(typeof(TAttribute), false))
Add(attr, member);
if (member.MemberType == MemberTypes.Method)
foreach (var parameter in ((MethodInfo)member).GetParameters())
foreach (TAttribute attr in parameter.GetCustomAttributes(typeof(TAttribute), false))
Add(attr, parameter);
}
}
}
foreach (var assemblyName in assembly.GetReferencedAssemblies())
{
if (!skipAssemblies.Contains(assemblyName.FullName))
ScanAssembly(Assembly.Load(assemblyName));
}
}
/// <summary>
/// Returns the target of an attribute.
/// </summary>
/// <param name="attribute">The attribute for which a target is sought</param>
/// <returns>The target of the attribute - either an Assembly, Type or MemberInfo instance.</returns>
public static object GetTarget(TAttribute attribute)
{
object result;
if (!targetMap.TryGetValue(attribute, out result))
{
// Since types can be loaded at any time, recheck that all assemblies are included...
// Walk up the stack in a last-ditch effort to find instances of the attribute.
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
// write call stack method names
foreach (StackFrame stackFrame in stackFrames)
{
Console.WriteLine(stackFrame.GetMethod().Name); // write method name
ScanAssembly(stackFrame.GetMethod().GetType().Assembly);
}
if (!targetMap.TryGetValue(attribute, out result))
throw new InvalidProgramException("Cannot find assembly referencing attribute");
}
return result;
}
/// <summary>
/// Static constructor for type.
/// </summary>
static AttributeTargetHelper()
{
targetMap = new Dictionary<TAttribute, object>();
// Do not load any assemblies reference by the assembly which declares the attribute, since they cannot possibly use the attribute
skipAssemblies = new List<string>(typeof(TAttribute).Assembly.GetReferencedAssemblies().Select(c => c.FullName));
// Skip common system assemblies
skipAssemblies.Add("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
skipAssemblies.Add("System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
skipAssemblies.Add("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
skipAssemblies.Add("System.Data.SqlXml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
skipAssemblies.Add("System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
skipAssemblies.Add("System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
// Scan the entire application
ScanAssembly(Assembly.GetEntryAssembly());
}
}
/// <summary>
/// Extends attributes so that their targets can be discovered
/// </summary>
public static class AttributeTargetHelperExtension
{
/// <summary>
/// Gets the target of an attribute
/// </summary>
/// <typeparam name="TAttribute"></typeparam>
/// <param name="attribute">The attribute for which a target is sought</param>
/// <returns>The target of the attribute - either an Assembly, Type or MemberInfo instance.</returns>
public static object GetTarget<TAttribute>(this TAttribute attribute)
where TAttribute : Attribute
{
return AttributeTargetHelper<TAttribute>.GetTarget(attribute);
}
}
}
덕분에이 작품의 "DoSomethingWithDeclaringType"종류 때까지 기다리는 방법,하지만 그것을 어떻게 든 것을 강요 의미 – Heka
속성 주된 용도는 주석입니다. 그 클래스/메소드/속성은이 방법으로 처리되어야하지만 실제로 모든 것을 분석하는 코드는 다른 장소에 있어야합니다. 따라서 속성 내부에서 무언가를하려는 의도는이 패턴을 깨뜨리는 것입니다. 조언 : 다른 해결책을 고려하십시오.
확인은 DoSomethingWithDeclaringType에 대한 첫 번째 호출에서만 수행되며, 아무도 직접 속성을 조회하지 않고 승인 기능을 거치지 않고 무언가를 수행하도록합니다. 가능한 경우이 유효성 검사 코드를 속성 자체에 넣을 것입니다. 가능한 경우 패턴을 위반할 것이라고 생각하지 않습니다. – DMC
속성이 한 클래스와 다른 클래스 사이의 관계를 설정한다고 가정합니다. "네,이 두 클래스는 실제로 서로 호환이됩니까?"라는 인증 코드는 어디에 두어야합니까? 속성이 설정 될 때로드 타임에 한 번만 해당 코드를 실행하기를 원하며 속성을 추가하는 모든 클래스에 의존하지 않고 코드의 연결 유효성을 확인합니다. "DoSomethingWithDeclaringType"종류의 작업이 시작될 때까지 대기하는 방식이지만 DoSomethingWithDeclaringTyp에 대한 첫 번째 호출에서만 검사가 수행된다는 것을 의미합니다. – DMC
또한 속성 생성자에 선언 유형을 제공하여 해당 유형에 대한 액세스를 제공 할 수 있습니다. 예 : ' class MyAttribute : Attribute { private void MyAttribute (Type declaringType) { // 여기에 선언 형에 액세스합니다. 필요하면 참조를 유지하십시오. } } 이 [MyAttribute (대해서 typeof (TARGETCLASS가))] 클래스 TARGETCLASS { } 솔루션에 대한 ' – Jaans