【问题标题】: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; 的任何类型的实例?

【问题讨论】:

    标签: c#


    【解决方案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
        

        【讨论】:

          猜你喜欢
          • 2022-11-05
          • 1970-01-01
          • 1970-01-01
          • 2017-04-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-26
          • 1970-01-01
          相关资源
          最近更新 更多