在 C# 中运行时查找对象的所有 references/declarations |结构图

Find all references/declarations of an object at runtime in c# | structuremap

我有一个 class LanguagePopupMessage 用于整个应用程序(和外部库)。如果构造此 class,它会获取创建它的命名空间并添加一个后缀以使其唯一。

问题是:如何获取所有 LanguagePopupMessage 定义,包括 fieldname 参数?

我在我的应用程序中使用 structuremap。它也在启动时扫描所有库,所以也许有可能如何自动化它。

using System;
using System.Diagnostics;

namespace ConsoleApp1
{
    /// <summary>
    /// Creates the namespace for a popup window and has an additional flag for the caption
    /// </summary>
    public class LanguagePopupMessage
    {
        public string Namespace { get; }
        public string Caption => $"{Namespace}Caption";

        public LanguagePopupMessage(string fieldName)
        {
            if(string.IsNullOrEmpty(fieldName))
                throw new ArgumentNullException(nameof(fieldName));
            if (_GetNamespace() is Type type)
            {
                Namespace = $"{type}.{fieldName}";
            }
            else
            {
                throw new InvalidOperationException("could not fetch the namespace");
            }
        }

        private Type _GetNamespace()
        {
            StackTrace st = new StackTrace();
            foreach (var sf in st.GetFrames())
            {
                var type = sf.GetMethod().DeclaringType;
                if (type != GetType())
                {
                    return type;
                }
            }
            return null;
        }

        public override string ToString()
        {
            return $"Namespace '{Namespace}' Caption '{Caption}'";
        }
    }

    class Program
    {
        //ConsoleApp1.Program.PopupMessage.ConfigNotLoaded
        //ConsoleApp1.Program.PopupMessage.ConfigNotLoadedCaption
        private static readonly LanguagePopupMessage _CONFIG_NOT_LOADED_POPUP_MESSAGE = new LanguagePopupMessage("ConfigNotLoaded");

        static void Main(string[] args)
        {
            Console.ReadKey();
        }
    }
}

namespace ConsoleApp1.Subfolder
{
    public class SubfolderClass
    {
        /// <summary>
        /// ConsoleApp1.Subfolder.SubfolderClass.FooMessage
        /// ConsoleApp1.Subfolder.SubfolderClass.FooMessageCaption
        /// </summary>
        public static readonly LanguagePopupMessage Message = new LanguagePopupMessage("FooMessage");
    }
}

我为 structuremap 定制了 IRegistrationConvention - FindAllLanguagePopupMessages。在运行时,一个新的 container 扫描所有库 -> 所有 Types 如果有 FieldInfo 类型 LanguagePopupMessage 并将其添加到集合中。

为了获得更好的性能,我做了一个 Attribute - ContainsTranslationDefinition 来过滤 classes.

源代码

public class ContainsTranslationDefinition : Attribute
{ }
/// <summary>
/// Creates the namespace for a popup window and has an additional flag for the caption
/// </summary>
public class LanguagePopupMessage
{
    public string Namespace { get; }
    public string Caption => $"{Namespace}Caption";

    public LanguagePopupMessage(string fieldName)
    {
        if(string.IsNullOrEmpty(fieldName))
            throw new ArgumentNullException(nameof(fieldName));
        if (_GetNamespace() is Type type)
        {
            Namespace = $"{type}.{fieldName}";
        }
        else
        {
            throw new InvalidOperationException("could not fetch the namespace");
        }
    }

    private Type _GetNamespace()
    {
        StackTrace st = new StackTrace();
        foreach (var sf in st.GetFrames())
        {
            var type = sf.GetMethod().DeclaringType;
            if (type != GetType())
            {
                return type;
            }
        }
        return null;
    }

    public override string ToString()
    {
        return $"Namespace '{Namespace}' Caption '{Caption}'";
    }
}

/// <summary>
/// Add <see cref="LanguagePopupMessage"/> into the <see cref="Container.Model"/> type lifecycle
/// </summary>
public class FindAllLanguagePopupMessages : IRegistrationConvention
{
    private readonly ILifecycle _Lifecycle = new UniquePerRequestLifecycle();

    public void ScanTypes(TypeSet types, Registry registry)
    {
        List<LanguagePopupMessage> dec = new List<LanguagePopupMessage>();
        foreach (Type type in types.AllTypes())
        {
            if (!Attribute.IsDefined(type, typeof(ContainsTranslationDefinition)))
            {
                continue;
            }
            _FindConfigDeclarations(type, dec);
        }

        foreach (LanguagePopupMessage languagePopupMessage in dec)
        {
            Console.WriteLine($"{languagePopupMessage}");
        }
    }

    private static void _FindConfigDeclarations(Type type, List<LanguagePopupMessage> declarations)
    {
        var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);

        declarations.AddRange(fields
            .Where(info => info.IsInitOnly && typeof(LanguagePopupMessage).IsAssignableFrom(info.FieldType))
            .Select(info => (LanguagePopupMessage)info.GetValue(null)));

        // find all nested class types and run method recursively
        foreach (var nestedType in type.GetNestedTypes(BindingFlags.Public))
        {
            _FindConfigDeclarations(nestedType, declarations);
        }
    }

}
[ContainsTranslationDefinition]
public class TestClass
{
    private static readonly LanguagePopupMessage _CONFIG_1 = new LanguagePopupMessage("ConfigNotLoaded1");

    private static readonly LanguagePopupMessage _CONFIG_2 = new LanguagePopupMessage("ConfigNotLoaded2");
}

[ContainsTranslationDefinition]
public class Program
{
    //ConsoleApp1.Program.PopupMessage.ConfigNotLoaded
    //ConsoleApp1.Program.PopupMessage.ConfigNotLoadedCaption
    private static readonly LanguagePopupMessage _CONFIG_NOT_LOADED_POPUP_MESSAGE = new LanguagePopupMessage("ConfigNotLoaded3");


    static void Main(string[] args)
    {
        // Create container and tell where to look for depencies
        IContainer container = new Container(c => c.Scan(scanner =>
        {
            scanner.TheCallingAssembly();
            scanner.WithDefaultConventions();
            scanner.AssembliesFromApplicationBaseDirectory();
            scanner.With(new FindAllLanguagePopupMessages());
        }));

        Console.ReadKey();
    }
}

预览