代码之家  ›  专栏  ›  技术社区  ›  Kjara

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

  •  0
  • Kjara  · 技术社区  · 6 年前

    Class1 有绳子吗 hello , Class2 有绳子吗 world 等等)。然后我将有一个泛型类型参数 T (在运行时)这些类中的一个。我需要能够从泛型类型参数中检索关联的字符串。

    我如何设置并使其工作?

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

    我尝试创建一个基类,该基类有一个包含字符串的公共静态字段,并为每个实际类“覆盖”(隐藏基类并创建新的)字符串。但是,当我只有type参数时,仍然无法检索字符串 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!
        }
    }
    

    真实世界用例:

    我有一节静态课 MySerializer<T> 它应该反序列化类型为的对象 T . 在反序列化过程中,我想验证 T 符合与类型关联的架构 T .

    T 可以反序列化的是,我在项目中存储了一个不同的模式作为嵌入式资源,因此每个模式都有一个路径(类似于文件路径)。这意味着:每节课 T 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);
            }
        }
    }
    
    2 回复  |  直到 6 年前
        1
  •  2
  •   Kjara    6 年前

    由于静态方法和泛型类型参数在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;
        }
    }
    
        2
  •  1
  •   Matthew Watson    6 年前

    使用反射可以做到这一点,但请注意,所有属性都需要是静态的(上面的示例代码向派生类引入了非静态属性)。

    下面是一个可编译的示例:

    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 实际上并不需要它来编译和工作,但是您可能希望保留它,以明确它只应该用于继承自的类 BaseClass