2011-06-10 86 views
3
public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source) where TV : new(TU) 
{ 
    return source.Select(x => new TV(TU)); 
} 

問題是我不能給出新的(TU)約束。類型參數構造函數簽名約束

  • 有沒有解決這個問題的方法?
+0

你只能指定默認的構造函數,帶參數的構造函數在C#中是不允許的。 – 2011-06-10 08:09:22

+0

@Srinivas是的,我注意到了。但是有沒有什麼解決這個問題的原因? – 2011-06-10 08:11:02

回答

1

也許最簡單的方法是在調用的地方明確地將公式從TU改爲TV,就像後面的選項1一樣。如果您希望隱藏場景背後的轉換細節,以使其在您調用擴展方法的任何位置工作以避免重複公式,那麼接口是適當的,因爲它可以用作擴展方法的約束:

class Program 
{ 
    static void Main(string[] args) 
    { 
     IEnumerable<U> uSequence = new List<U>(); 
     IEnumerable<V> vSequence1 = uSequence.To(u => new V(u)); //Option 1 : explicit transformation, needs neither any interface nor explicit types (type inference at work) 
     IEnumerable<V> vSequence2 = uSequence.To<U, V>(); //Option 2 : implicit transformation internally supported from U to V by type V thanks to IBuildableFrom<TV, TU>, but you must precise To<U, V>() with the types 
    } 
} 

public static class Extensions { 
    //Option 1 
    public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source, Func<TU,TV> transform) 
    { 
     return source.Select(tu => transform(tu)); 
    } 

    //Option 2 
    public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source) where TV : IBuildableFrom<TV, TU>, new() 
    { 
     return source.Select(tu => new TV().BuildFrom(tu)); 
    } 
} 

public interface IBuildableFrom<TV, TU> 
{ 
    TV BuildFrom(TU tu); 
} 

public class U { } //Cheesy concrete class playing the rôle of TU 
public class V : IBuildableFrom<V, U> //Cheesy concrete class playing the rôle of TV 
{ 
    public V BuildFrom(U u) 
    { 
     //Initialization of this' properties based on u's ones 
     return this; 
    } 

    public V(U u) { }//Used by option 1 
    public V() { } //Used by option 2 
} 
3

也許傳遞函數求能夠從TU製作電視:

public static IEnumerable<TV> To<TU, TV>(
    this IEnumerable<TU> source, 
    Func<TU, TV> builder) 
    where TV : class 
{ 
    return source.Select(x => builder(x)); 
} 

tus.To(x => new TV(x)); 
+0

+1與我的想法完全不同,但非常有趣。 – 2011-06-10 08:17:10

+0

@Amir是的,我自己也不太確定,一方面它提供瞭如何從擴展方法創建電視對象到調用者的知識,另一方面它在電視對象的方式上更加靈活創建。 – 2011-06-10 08:19:42

+0

@Graham Clark:你的實現可以縮短爲「return source.Select(builder)」。這與使用Enumerable.Select直接「tus.Select(x => new TV(x))」有什麼不同? – 2011-06-10 08:39:30

4

我要告訴你兩種方法調用:

首先使用Activator.CreateInstance

public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source) 
{ 
    return source.Select(m => (TV) Activator.CreateInstance(typeof(TV), m)); 
} 

其次,你可以使用接口來定義屬性,而不是使用參數的構造函數:

public interface IRequiredMember 
{} 

public interface IHasNeccesaryMember 
{ 
    IRequiredMember Member 
    { 
    get; 
    set; 
    } 
} 

public static IEnumerable<TV> To<TU, TV>(this IEnumerable<TU> source) 
      where TV : IHasNeccesaryMember, new() 
      where TU : IRequiredMember 
{ 
    return source.Select(m => new TV{ Member = m }); 
} 

第一種方法的作品,但感覺很髒,有越來越構造函數調用錯誤的風險,尤其是在方法不受限制。

因此,我認爲第二種方法是一個更好的解決方案。

+0

第二種方法看起來非常好! – 2011-06-10 08:30:33

+0

如何將會員更改爲電視的構造函數? – 2011-06-10 08:32:16

+0

@Amir:你不能使用接口來定義構造函數 - 例如參見ISerializable,所以成員規範儘可能接近我的想法。 – Xhalent 2011-06-10 09:05:22

相關問題