2009-06-14 11 views
2

私はC#コードに苦労していますが、問題の解決策を見つけましたが、それは決して理想的ではありません(後述のDoSomething_WorksButNotIdeal()を参照)。C#キャスティングとジェネリックス

私がしたいのは、if、elseステートメント(サポートしたいタイプに応じて大量になる可能性があります)を持つ代わりに、汎用キャストがあるだけですが、動作させることができません。私はこれをDoSomething_HelpMe()メソッドで実証しようとしました。

これを達成するにはどうしますか?どんな助けでも大歓迎です。

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

をし、それをより具体的にするためにITag<T>を使用します。あなたがすることはできません

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<T> : ITag 
{ 
    new T InMemValue { get; set; } 
    new T OnDiscValue { get; set; } 
} 

この方法ではITagを使用できます。

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); 

(多分あなたは(Tag<T>をint型)のようなものを必要とするだろうコピー先とは何かについてより具体的に)

具体的な実装(Tag<T>)のITagが実際にITag<T>キャストであると仮定しなければならない。それを解決する最も簡単な方法は、すなわちTag<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

Marc、ご返信いただきありがとうございます。両方の提案は可能な解決策です:-) 文法的には、私のクラスをCopy *メソッドで駄目にしないので、私は第1の提案を好むが、どちらが良いかを知ることに興味がある。私はそれがただ一つのダウンキャストを持っているので、第2を推測しています。 1つ目は、参照変数をダウンキャストしアップキャストしなければならず、それが値型の場合はさらに悪いボックスとunboxです。 –

3

boolは本当に醜いANあるので、あなたが本当に、代わりにsourceIsMemtargetIsMemためenumタイプを使用する必要があります(追加のみを示す!)

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; 
    } 
} 

既存のタイプに以下の注意事項次のフラグメントが示すように、呼び出す際に読みにくい。

これは、あなたが今、あなたの日常の仕事になるだろうかです:

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

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); 

     } 
    } 
} 
関連する問題