簡體   English   中英

C#強制轉換和泛型

[英]C# Casting and Generics

我一直在努力處理一段C#代碼,盡管我找到了解決問題的方法,但這絕不是理想的方法(請參見下面的DoSomething_WorksButNotIdeal())。

我想做的是,而不是使用if,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;
        }
    }
}

您無法擁有的任何原因:

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

(關於它復制到/從中復制的內容可能更具體)

您的具體實現( Tag<T> )必須假定ITag實際上是ITag<T>並進行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;
}

解決該問題的最簡單方法是解析您擁有信息的類型,即在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;
    }
}

請注意,實際上您應該對sourceIsMemtargetIsMem使用enum類型,因為在調用中bool確實很丑陋且很難閱讀,如以下片段所示。

這是您現在使例行工作的方式:

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

這是一種方法,它需要大量樣板文件,但允許您使用ITagITag<T>Tag<T>現有定義來ITag ITag<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);

        }
    }
}

暫無
暫無

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

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