簡體   English   中英

C# 中是否有帶參數約束的泛型構造函數?

[英]Is there a generic constructor with parameter constraint in C#?

在 C# 中,您可以對泛型方法施加約束,例如:

public class A {

    public static void Method<T> (T a) where T : new() {
        //...do something...
    }

}

您指定T應該有一個不需要參數的構造函數。 我想知道是否有辦法添加約束,例如“存在帶有float[,]參數的構造函數?

以下代碼無法編譯:

public class A {

    public static void Method<T> (T a) where T : new(float[,] u) {
        //...do something...
    }

}

解決方法也有用嗎?

正如你所發現的,你不能這樣做。

作為一種解決方法,我通常提供一個可以創建T類型對象的委托:

public class A {

    public static void Method<T> (T a, Func<float[,], T> creator) {
        //...do something...
    }

}

使用反射創建泛型對象,該類型仍然需要聲明正確的構造函數,否則將引發異常。 您可以傳入任何參數,只要它們與構造函數之一匹配即可。

使用這種方式,您不能對模板中的構造函數施加約束。 如果缺少構造函數,則需要在運行時處理異常,而不是在編譯時出錯。

// public static object CreateInstance(Type type, params object[] args);

// Example 1
T t = (T)Activator.CreateInstance(typeof(T));
// Example 2
T t = (T)Activator.CreateInstance(typeof(T), arg0, arg1, arg2, ...);
// Example 3
T t = (T)Activator.CreateInstance(typeof(T), (string)arg0, (int)arg1, (bool)arg2);

沒有這樣的構造。 您只能指定一個空的構造函數約束。

我用 lambda 方法解決了這個問題。

public static void Method<T>(Func<int,T> del) {
  var t = del(42);
}

用例

Method(x => new Foo(x));

這是我個人認為非常有效的解決方法。 如果您考慮什么是泛型參數化構造函數約束,它實際上是具有特定簽名的類型和構造函數之間的映射。 您可以使用字典創建自己的此類映射。 將它們放在靜態“工廠”類中,您可以創建不同類型的對象,而不必擔心每次都構建構造函數 lambda:

public static class BaseTypeFactory
{
   private delegate BaseType BaseTypeConstructor(int pParam1, int pParam2);

   private static readonly Dictionary<Type, BaseTypeConstructor>
   mTypeConstructors = new Dictionary<Type, BaseTypeConstructor>
   {
      { typeof(Object1), (pParam1, pParam2) => new Object1(pParam1, pParam2) },
      { typeof(Object2), (pParam1, pParam2) => new Object2(pParam1, pParam2) },
      { typeof(Object3), (pParam1, pParam2) => new Object3(pParam1, pParam2) }
   };

然后在您的通用方法中,例如:

   public static T BuildBaseType<T>(...)
      where T : BaseType
   {
      ...
      T myObject = (T)mTypeConstructors[typeof(T)](value1, value2);
      ...
      return myObject;
   }

不。目前您可以指定的唯一構造函數約束是無參數構造函數。

我認為這是對對象構造方式施加限制的最干凈的解決方案。 它沒有完全檢查編譯時。 當您同意使類的實際構造函數具有與 IConstructor 接口相同的簽名時,這有點像對構造函數施加約束。 由於顯式接口實現, Constructor方法在與對象正常工作時是隱藏的。

using System.Runtime.Serialization;

namespace ConsoleApp4
{
    class Program
    {
        static void Main(string[] args)
        {
            var employeeWorker = new GenericWorker<Employee>();
            employeeWorker.DoWork();
        }
    }

    public class GenericWorker<T> where T:IConstructor
    {
        public void DoWork()
        {
            T employee = (T)FormatterServices.GetUninitializedObject(typeof(T));
            employee.Constructor("John Doe", 105);
        }
    }

    public interface IConstructor
    {
        void Constructor(string name, int age);
    }

    public class Employee : IConstructor
    {
        public string Name { get; private set; }
        public int Age { get; private set; }

        public Employee(string name, int age)
        {
            ((IConstructor)this).Constructor(name, age);
        }

        void IConstructor.Constructor(string name, int age)
        {
            Name = name;
            Age = age;
        }
    }
}

如何創建帶有約束的泛型類,這里我選擇了 struct 和 class 來具有值和引用類型。

這樣你的構造函數就對值有約束。

 class MyGenericClass<T, X> where T :struct where X: class { private T genericMemberVariableT; private X genericMemberVariableX; public MyGenericClass(T valueT, X valueX) { genericMemberVariableT = valueT; genericMemberVariableX = valueX; } public T genericMethod(T genericParameter) { Console.WriteLine("Parameter type: {0}, value: {1}", typeof(T).ToString(), genericParameter); Console.WriteLine("Return type: {0}, value: {1}", typeof(T).ToString(), genericMemberVariableT); Console.WriteLine("Return type: {0}, value: {1}", typeof(X).ToString(), genericMemberVariableX); return genericMemberVariableT; } public T genericProperty { get; set; } }

執行:

 MyGenericClass<int, string> intGenericClass = new MyGenericClass<int, string>(10, "Hello world"); int val = intGenericClass.genericMethod(200);

這是 c# 維護者推薦的解決方法,如果您想保留構造函數參數,請間接調用構造函數:

            i = (TService)Activator.CreateInstance(typeof(TService), new object[] {arg});

其中 TService 是一個泛型,帶有我想保留的全參數構造函數。

如果您想了解此方法的工作原理: https : //docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance?view=net-5.0#system-activator-createinstance(系統類型系統對象-)

Aaaa 和 C# 維護者的討論: https : //github.com/dotnet/csharplang/discussions/769

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM