这个嵌套类构造函数片段可以应用于泛型类吗?

Posted

技术标签:

【中文标题】这个嵌套类构造函数片段可以应用于泛型类吗?【英文标题】:Can this nested class constructor snippet apply to a generic class? 【发布时间】:2017-03-13 00:35:30 【问题描述】:

Here 是一个高度赞成的解决方案,可以轻松地将嵌套类构造函数限制为仅父类。我正在尝试实现这一点,但在包含嵌套 generic 类的父类的上下文中。为了清楚起见,这里是原始的 sn-p:

public class Journal

  private static Func<object, JournalEntry> _newJournalEntry;

  public class JournalEntry
  
    static JournalEntry()
    
       _newJournalEntry = value => new JournalEntry(value);
    
  private JournalEntry(object value)
  
     ...

这是我目前所在的位置(为简单起见,我将 Func&lt;object,JournalEntry&gt; 替换为简单的 Func&lt;JournalEntry&gt;。)

public class Journal

    private static Func<JournalEntry> _new;

    public JournalEntry<T> NewJournalEntry<T>()
    
        return (JournalEntry<T>)_new();            
    

    public Journal()

    public class JournalEntry 

    public class JournalEntry<T>:JournalEntry
    
        static JournalEntry()
        
            _new = () => new JournalEntry<T>();
        

        private JournalEntry()
        

        
    

这是用例:

Journal j = new Journal();
Journal.JournalEntry<string> stringEntry = j.NewJournalEntry<string>();
//Fails with null reference exception

根据原始 sn-p 的此评论,

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConst‌​ructor 可以挽救这一天。 :)

我试了一下:

Journal j = new Journal();
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Journal.JournalEntry<string>).TypeHandle);
Journal.JournalEntry<string> stringEntry = j.NewJournalEntry<string>();
//Passes

上面的代码有效。以下作品:

Journal j = new Journal();
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Journal.JournalEntry<string>).TypeHandle);

Journal.JournalEntry<string> stringEntry = j.NewJournalEntry<string>();
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Journal.JournalEntry<int>).TypeHandle);

Journal.JournalEntry<int> intEntry = j.NewJournalEntry<int>();

但尝试请求另一个 JournalEntry&lt;string&gt; 失败:

附加信息:无法将 JournalEntry[System.Int32] 类型的对象转换为 JournalEntry[System.String] 类型。

我该如何解决这个难题,以便我可以创建任意数量的 JournalEntry&lt;&gt; 的任何类型的实例?

【问题讨论】:

【参考方案1】:

您的方法行不通,因为每次 JournalEntry&lt;T&gt; 的类型初始化程序运行(并且每个 T 运行一次)时,它都会覆盖 _new 的先前值,从而破坏未来的使用其他Ts.

相反,您可以这样做:

public class Journal

    public JournalEntry<T> NewJournalEntry<T>()
    
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(JournalEntry<T>).TypeHandle);
        return JournalEntryFactory<T>._new();
    

    private static class JournalEntryFactory<T>
    
        public static Func<JournalEntry<T>> _new;
    

    public Journal()  

    public class JournalEntry  

    public class JournalEntry<T> : JournalEntry
    
        static JournalEntry()
        
            JournalEntryFactory<T>._new = () => new JournalEntry<T>();
        

        private JournalEntry()
        

        
    

这样,每个T 都有一个单独的_new

顺便说一句,我是问你提到的问题的人。对于它的价值,我认为这种方法有点骇人听闻且容易出错。如果可能的话,我强烈建议您使用accepted answer 中的基于接口的方法。以下是您的情况:

public interface IJournalEntry<T>



public class Journal

    public IJournalEntry<T> NewJournalEntry<T>()
    
        return new JournalEntry<T>();
    

    public Journal()  

    private class JournalEntry<T> : IJournalEntry<T>
    
        public JournalEntry()
        

        
    

【讨论】:

使用接口方法的原始接受答案中没有立即显示的一个特定信息:使用 public 构造函数将嵌套类定义 private ,并且奇迹般地只有外部类可以调用构造函数,正如那里的评论者所指出的那样。我可以请求一个最终干净的 sn-p 你是如何使用接口方法的吗?【参考方案2】:

您需要多个new 方法。我建议将它们保存在 Dictionary 中,如下所示:

public class Journal

    private static Dictionary<Type, Func<JournalEntry>> _newFuncs = new Dictionary<System.Type, System.Func<UserQuery.Journal.JournalEntry>>();

    public JournalEntry<T> NewJournalEntry<T>()
    
        var newFunc = _newFuncs[typeof(T)];
        return (JournalEntry<T>)newFunc();
    

    public Journal()  

    public class JournalEntry  

    public class JournalEntry<T> : JournalEntry
    
        static JournalEntry()
        
            _newFuncs.Add(typeof(T), () => new JournalEntry<T>());
        

        private JournalEntry()
        

        
    

【讨论】:

【参考方案3】:

您可以修改您的类,以便将_new 作为字典,这将允许创建多种类型:

public class Journal

    private static IDictionary<Type, Func<JournalEntry>> _new = new Dictionary<Type, Func<JournalEntry>>();

    public JournalEntry<T> NewJournalEntry<T>()
    
        return (JournalEntry<T>)_new[typeof(T)]();
    

    public Journal()  

    public class JournalEntry  

    public class JournalEntry<T> : JournalEntry
    
        static JournalEntry()
        
            _new.Add(typeof(T), () => new JournalEntry<T>());
        

        private JournalEntry()
        

        
    


【讨论】:

【参考方案4】:

不能很容易地完全按照您的要求完成(通用类实际上是一组类,因此您需要为_new 提供多个代表,并通过某种方式来选择一个)但这里有一个非常完成您想要的事情的简单方法,无需使用 hacky 静态构造函数和委托,也无需使用单独的接口。

本质上,我们创建了一个受保护的工厂方法,然后使用该方法的公共版本创建该类的私有派生。受保护的工厂方法只能从我们的私有类中调用。

public class Journal

    public class JournalEntry  

    public class JournalEntry<T> : JournalEntry
    
        protected JournalEntry()
        
        

        static protected JournalEntry<T> NewJournalEntry()
        
            return new JournalEntry<T>();
        
    

    private class Maker<T> : JournalEntry<T>
    
        new static public JournalEntry<T> NewJournalEntry()
        
            return JournalEntry<T>.NewJournalEntry();
        
    

    public JournalEntry<T> NewJournalEntry<T>()
    
        return Maker<T>.NewJournalEntry();
    

然后您可以像这样创建一个新实例(完全按照您的要求):

Journal j = new Journal();
Journal.JournalEntry<string> stringEntry = j.NewJournalEntry<string>();

但是不能直接实例化:

Journal.JournalEntry<string> wontWork = new Journal.JournalEntry<string>();  //is inaccessible due to its protection level

【讨论】:

以上是关于这个嵌套类构造函数片段可以应用于泛型类吗?的主要内容,如果未能解决你的问题,请参考以下文章

c#中泛型类构造函数重载赋值时为啥不接受null?对其赋空值应给怎么做?

Kotlin泛型 ① ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 )

C# 泛型类 构造方法中实例化T

在Java泛型类中,在构造函数级别添加额外的通用约束?

如何初始化这个泛型类

关于泛型