2009-06-14 101 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); 

     } 
    } 
}