C#连铸泛型
我已经挣扎用一块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;
}
}
}
任何原因,你不能有:
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;
}
来解决这个问题是要解决,你掌握的信息,即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
类型的sourceIsMem
和targetIsMem
而是因为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);
}
Ack ...我刚刚发现这就是Marc的编辑提议的:)我应该在确定有更好的答案之前阅读全文答案。 – jerryjvl 2009-06-14 13:35:07
这里有一个方法,这需要样板,像样的数目,但可以让你做你想要使用的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);
}
}
}
马克,非常感谢您的回复。这两个建议是可行的解决方案:-) 语法上我更喜欢第一个建议,因为它不会掉落在我的类复制*方法,但是我很想知道哪一个执行得更好。我猜第二,因为它只有一个沮丧。如果它是一个值类型,第一个将不得不向下参考变量的downcast和upcast,如果它是一个值类型,则会变得更差并且取消box。 – 2009-06-16 20:34:13