将字符串与 class 相关联,以便在 class 作为泛型类型参数给出时我可以检索该字符串

associate a string with a class so that I can retrieve the string if class is given as generic type parameter

对于不同的 classes 我需要在 class 上附加一个字符串(即 Class1 有字符串 helloClass2 有字符串world 等)。然后我将在某个地方有一个泛型类型参数 T,它将是(在运行时)这些 class 之一。我需要能够从该泛型类型参数中检索关联的字符串。

我该如何设置它并让它发挥作用?

因为所有 class 都是我写的,我可以使用所有可能的方法(例如为它们定义公共接口或公共基础 class 或其他)。

我尝试创建一个基础 class,它有一个包含字符串的 public 静态字段,并且对于每个实际的 class "overwrite"(隐藏基础并创建新的)字符串。但事实证明,当只有类型参数 T.

时,我仍然无法检索字符串
public class BaseClass 
{ 
    public static string Get => ""; 
}

public class Class1 : BaseClass 
{ 
    public static new string Get => "hello"; 
} 

public class Class2 : BaseClass 
{ 
    public static new string Get => "world"; 
}

public class Testing<T> where T : BaseClass
{
    public void Test()
    {
        string s = T.Get;
        // compiler error: "'T' is a type parameter, which is not valid in the given context"
        // strangely though, BaseClass.Get and Class1.Get and Class2.Get work fine!
    }
}

实际用例:

我有一个静态 class MySerializer<T> 应该反序列化 T 类型的对象。在反序列化期间,我想验证我的 T 类型的对象是否符合与 T.

类型关联的模式

为了验证我需要先添加一个模式。对于每个可以反序列化的 class T 都有一个不同的模式,我将其作为嵌入式资源存储在我的项目中,因此每个模式都有一个路径(如文件路径)。这意味着:对于每个 class T,我需要将一个字符串(路径)与 class 相关联,以便我能够从 T 中获取该路径。

这是我的序列化程序和模式添加过程的相关部分:

public static class MySerializer<T>
{
    private static readonly XmlSerializer _mySerializer = new XmlSerializer(typeof(T));
    private static readonly XmlReaderSettings _settings = new Func<XmlReaderSettings>(() =>
    {
        System.Reflection.Assembly assy = typeof(MySerializer<T>).Assembly;
        XmlSchemaSet schemas = new XmlSchemaSet();
        schemas.Add(null,
            XmlReader.Create(assy.GetManifestResourceStream(T.GetAssociatedString())));
            // T.GetAssociatedString(): How to make this work?
        return new XmlReaderSettings
        {
            Schemas = schemas,
            ValidationType = ValidationType.Schema,
            ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings |
                XmlSchemaValidationFlags.ProcessIdentityConstraints
        };
    })();

    public static T Deserialize(Stream strm)
    {
        using (XmlReader reader = XmlReader.Create(strm, _settings))
        {
            return (T)_mySerializer.Deserialize(reader);
        }
    }
}

可以使用反射来做到这一点,但请注意所有属性都必须是静态的(您上面的示例代码将非静态属性引入派生的 class).

这是一个可编译的例子:

using System;

namespace Demo
{
    public class BaseClass
    {
        public static string Get => "";
    }

    public class Class1 : BaseClass
    {
        public new static string Get => "hello";
    }

    public class Class2 : BaseClass
    {
        public new static string Get => "world";
    }

    public class Testing<T> where T : BaseClass
    {
        public string Test()
        {
            var property = typeof(T).GetProperty("Get");

            if (property != null)
                return (string) property.GetValue(null, null);

            return null;
        }
    }

    class Program
    {
        static void Main()
        {
            var test1 = new Testing<Class1>();
            Console.WriteLine(test1.Test());  // Prints "hello"

            var test2 = new Testing<Class2>();
            Console.WriteLine(test2.Test()); // Prints "world"
        }
    }
}

在此代码中,编译和工作实际上不需要 where T : BaseClass,但您可能希望保留它以明确它仅应与 [=21 一起使用=]es 继承自 BaseClass.

由于静态方法和泛型类型参数在 C# 中不能一起工作(感谢 Matthew Watson 链接到 Eric Lippet 的博客)而且我不想创建 T 的新实例只是为了能够调用方法,我会用属性。

[AttributeUsage(AttributeTargets.Class)]
class SomeStringAttribute : Attribute
{
    public string SomeString { get; set; }

    public SomeStringAttribute(string s)
    {
        SomeString = s;
    }
}

[SomeString("hello")]
public class Class1
{ 
} 

[SomeString("world")]
public class Class2
{ 
}

public class Testing<T>
{
    public void Test()
    {
        string s =
            ((SomeStringAttribute)typeof(T).GetCustomAttributes(typeof(SomeStringAttribute),
                false)[0]).SomeString;
    }
}