2009-06-14 78 views
2

我已經掙扎用一塊C#代碼,雖然我已經找到了解決問題的方法,它絕不是理想的(參照DoSomething_WorksButNotIdeal()下文)。C#連鑄泛型

我想這樣做是不是具有如果else語句(這是潛在的巨大依賴於我要支持什麼類型的)只是有一個普通的演員,但我不能得到它的工作。我試圖在DoSomething_HelpMe()方法中演示這一點。

反正是有實現這一目標?任何幫助是極大的讚賞。

public interface ITag 
{ 
    string TagName { get; } 
    Type Type { get; } 
} 

public interface ITag<T> : ITag 
{ 
    T InMemValue { get; set; } 
    T OnDiscValue { get; set; } 
} 


public class Tag<T> : ITag<T> 
{ 
    public Tag(string tagName) 
    { 
     TagName = tagName; 
    } 

    public string TagName { get; private set; } 
    public T InMemValue { get; set; } 
    public T OnDiscValue { get; set; } 
    public Type Type{ get{ return typeof(T);} } 
} 

public class MusicTrack 
{ 
    public MusicTrack() 
    { 
     TrackTitle = new Tag<string>("TrackTitle"); 
     TrackNumber = new Tag<int>("TrackNumber"); 

     Tags = new Dictionary<string, ITag>(); 
     Tags.Add(TrackTitle.TagName, TrackTitle); 
     Tags.Add(TrackNumber.TagName, TrackNumber); 
    } 

    public IDictionary<string,ITag> Tags; 

    public ITag<string> TrackTitle { get; set; } 
    public ITag<int> TrackNumber { get; set; } 
} 


public static class Main 
{ 
    public static void DoSomething_WorksButNotIdeal() 
    { 
     MusicTrack track1 = new MusicTrack(); 
     MusicTrack track2 = new MusicTrack(); 

     // Set some values on the tracks 

     foreach (ITag tag in track1.Tags.Values) 
     { 
      Type type = tag.Type; 

      if (type == typeof(string)) 
      { 
       ((ITag<string>) tag).InMemValue = ((ITag<string>)track2.Tags[tag.TagName]).OnDiscValue; 
      } 
      else if (type == typeof(int)) 
      { 
       ((ITag<int>)tag).InMemValue = ((ITag<int>)track2.Tags[tag.TagName]).OnDiscValue; 
      } 
      else if (type == typeof(bool)) 
      { 
       ((ITag<bool>)tag).InMemValue = ((ITag<bool>)track2.Tags[tag.TagName]).OnDiscValue; 
      } 
      // etc etc 
      else 
      { 
       throw new Exception("Unsupported type."); 
      } 
     } 
    } 

    public static void DoSomething_HelpMe() 
    { 
     MusicTrack track1 = new MusicTrack(); 
     MusicTrack track2 = new MusicTrack(); 

     // Set some values on the tracks 

     foreach (ITag tag in track1.Tags.Values) 
     { 
      Type type = tag.Type; 

      // THIS OBVIOUSLY DOESN'T WORK BUT I'M JUST TRYING TO DEMONSTRATE WHAT 
      // I'D IDEALLY LIKE TO ACHIEVE 
      ((ITag<typeof(type)>)tag).InMemValue = ((ITag<typeof(type)>)track2.Tags[tag.TagName]).OnDiscValue; 
     } 
    } 
} 

回答

4

任何原因,你不能有:

public interface ITag 
{ 
    string TagName { get; } 
    Type Type { get; } 
    object InMemValue { get; set; } 
    object OnDiscValue { get; set; } 
} 

,並使用ITag<T>,使之更加具體些嗎?

public interface ITag<T> : ITag 
{ 
    new T InMemValue { get; set; } 
    new T OnDiscValue { get; set; } 
} 

然後你的方法可以使用ITag。你會需要像(INT Tag<T>):

object ITag.InMemValue 
{ 
    get { return InMemValue; } 
    set { InMemValue = (T)value; } 
} 
object ITag.OnDiscValue 
{ 
    get { return OnDiscValue; } 
    set { OnDiscValue = (T)value; } 
} 

(編輯)

另一種選擇將是對非通用ITag的方法:

void CopyValueFrom(ITag tag); 

(也許更具體一點什麼它拷貝到/從)

您的具體implementati上(Tag<T>)將不得不假設ITag實際上是一個ITag<T>和投:

public void CopyFromTag(ITag tag) { 
    ITag<T> from = tag as ITag<T>; 
    if(from==null) throw new ArgumentException("tag"); 
    this.TheFirstProperty = from.TheSecondProperty; 
} 
+0

馬克,非常感謝您的回覆。這兩個建議是可行的解決方案:-) 語法上我更喜歡第一個建議,因爲它不會掉落在我的類複製*方法,但是我很想知道哪一個執行得更好。我猜第二,因爲它只有一個沮喪。如果它是一個值類型,第一個將不得不向下參考變量的downcast和upcast,如果它是一個值類型,則會變得更差並且取消box。 – 2009-06-16 20:34:13

3

來解決這個問題是要解決,你掌握的信息,即Tag<T>裏面執行類型最簡單的方法,所以加下面以現有的類型(只顯示增加!)

public interface ITag 
{ 
    void CopyFrom(bool sourceIsMem, ITag sourceTag, bool targetIsMem); 
} 

public class Tag<T> : ITag<T> 
{ 
    public void CopyFrom(bool sourceIsMem, ITag sourceTag, bool targetIsMem) 
    { 
     ITag<T> castSource = sourceTag as ITag<T>; 
     if (castSource == null) 
      throw new ArgumentException(
       "Source tag is of an incompatible type", "sourceTag"); 

     if (targetIsMem) 
      InMemValue = sourceIsMem ? 
       castSource.InMemValue : castSource.OnDiscValue; 
     else 
      OnDiscValue = sourceIsMem ? 
       castSource.InMemValue : castSource.OnDiscValue; 
    } 
} 

請注意,你真的應該使用enum類型的sourceIsMemtargetIsMem而是因爲bool實在是太醜了d很難在調用中讀取,如下面的片段所示。

這是你將如何讓你的現在的日常工作:

public static void DoSomething_HelpMe() 
{ 
    MusicTrack track1 = new MusicTrack(); 
    MusicTrack track2 = new MusicTrack(); 

    // Set some values on the tracks 
    foreach (ITag tag in track1.Tags.Values) 
     tag.CopyFrom(false, track2.Tags[tag.TagName], true); 
} 
+0

Ack ...我剛剛發現這就是Marc的編輯提議的:)我應該在確定有更好的答案之前閱讀全文答案。 – jerryjvl 2009-06-14 13:35:07

0

這裏有一個方法,這需要樣板,像樣的數目,但可以讓你做你想要使用的ITag現有的定義是什麼,ITag<T> ,和Tag<T>TagSetter類以任何ITag<T>的類型安全方式從盤上值設置內存值。

/// <summary> 
/// Allows a tag of any type to be used to get a result of type TResult 
/// </summary> 
/// <typeparam name="TResult">The result type after using the tag</typeparam> 
public interface ITagUser<TResult> 
{ 
    TResult Use<T>(ITag<T> tag); 
} 

/// <summary> 
/// Allows a tag of any type to be used (with no return value) 
/// </summary> 
public interface ITagUser 
{ 
    void Use<T>(ITag<T> tag); 
} 

/// <summary> 
/// Wraps a tag of some unknown type. Allows tag users (either with or without return values) to use the wrapped list. 
/// </summary> 
public interface IExistsTag 
{ 
    TResult Apply<TResult>(ITagUser<TResult> user); 
    void Apply(ITagUser user); 
} 

/// <summary> 
/// Wraps a tag of type T, hiding the type itself. 
/// </summary> 
/// <typeparam name="T">The type of element contained in the tag</typeparam> 
class ExistsTag<T> : IExistsTag 
{ 

    ITag<T> tag; 

    public ExistsTag(ITag<T> tag) 
    { 
     this.tag = tag; 
    } 

    #region IExistsTag Members 

    public TResult Apply<TResult>(ITagUser<TResult> user) 
    { 
     return user.Use(tag); 
    } 

    public void Apply(ITagUser user) 
    { 
     user.Use(tag); 
    } 

    #endregion 
} 

public interface ITag 
{ 
    string TagName { get; } 
    Type Type { get; } 
} 

public interface ITag<T> : ITag 
{ 
    T InMemValue { get; set; } 
    T OnDiscValue { get; set; } 
} 


public class Tag<T> : ITag<T> 
{ 
    public Tag(string tagName) 
    { 
     TagName = tagName; 
    } 

    public string TagName { get; private set; } 
    public T InMemValue { get; set; } 
    public T OnDiscValue { get; set; } 
    public Type Type { get { return typeof(T); } } 
} 

public class TagSetter : ITagUser 
{ 
    #region ITagUser Members 

    public void Use<T>(ITag<T> tag) 
    { 
     tag.InMemValue = tag.OnDiscValue; 
    } 

    #endregion 
} 

public class TagExtractor : ITagUser<ITag> 
{ 
    #region ITagUser<ITag> Members 

    public ITag Use<T>(ITag<T> tag) 
    { 
     return tag; 
    } 

    #endregion 
} 

public class MusicTrack 
{ 
    public MusicTrack() 
    { 
     TrackTitle = new Tag<string>("TrackTitle"); 
     TrackNumber = new Tag<int>("TrackNumber"); 

     Tags = new Dictionary<string, IExistsTag>(); 
     Tags.Add(TrackTitle.TagName, new ExistsTag<string>(TrackTitle)); 
     Tags.Add(TrackNumber.TagName, new ExistsTag<int>(TrackNumber)); 
    } 

    public IDictionary<string, IExistsTag> Tags; 

    public ITag<string> TrackTitle { get; set; } 
    public ITag<int> TrackNumber { get; set; } 
} 

public static class Main 
{ 
    public static void DoSomething_WorksButNotIdeal() 
    { 
     MusicTrack track1 = new MusicTrack(); 
     MusicTrack track2 = new MusicTrack(); 

     TagSetter setter = new TagSetter(); 
     TagExtractor extractor = new TagExtractor(); 

     // Set some values on the tracks 

     foreach (IExistsTag tag in track1.Tags.Values) 
     { 
      tag.Apply(setter); 

      // do stuff using base interface if necessary 
      ITag itag = tag.Apply(extractor); 

     } 
    } 
}