我有以下类层次结构。
public abstract class ResourceBase { }
public abstract class WebResourceBase : ResourceBase {
public ResourceBase LocalPath { get; set; }
public ResourceBase FtpPath { get; set; }
}
public class JavaScript : WebResourceBase { }
我想做的是有这样的声明。
new JavaScript() {
LocalPath = "/path/goes/here/1.js",
FtpPath = "ftp://path/goes/here/1.js"
}
这里显而易见的答案是使用隐式运算符,但问题是我想将派生类型分配给那些与声明类型相同的属性,因此LocalPath
和FtpPath
将是类型为JavaScript
。
我希望我的解决方案比我现在的解决方案更灵活。这段代码让我的皮肤爬行。希望有一种方法可以使用反射,我尝试使用StackTrace
类来查找信息,但没有运气。任何帮助,将不胜感激。感谢。
public abstract class ResourceBase {
public static implicit operator ResourceBase(string path) {
if (path.EndsWith(".js"))
return new JavaScript(path);
// etc...
}
}
答案 0 :(得分:3)
这假设WebResourceBase
实际上是为了继承ResourceBase
。
不幸的是,你将无法使隐式运算符更好看 - 泛型在这里不起作用。
<小时/> 替代方案:限制ResourceBase的泛型
现在我已经重新阅读并理解你所追求的内容,一种选择是修改你的类以包含一个引用派生类的泛型参数(有点像自引用):
public abstract class ResourceBase
{ }
public abstract class WebResourceBase<T> : ResourceBase
where T : WebResourceBase<T>
{
public T LocalPath { get; set; }
public T FtpPath { get; set; }
}
public class JavaScript : WebResourceBase<JavaScript>
{
}
然后您会在JavaScript
中看到属性LocalPath
和FtpPath
现在也属于JavaScript
类型。
现在,您的作业仅接受JavaScript
类型:
new JavaScript()
{
LocalPath = new JavaScript("/path/goes/here/1.js"),
FtpPath = new JavaScript("ftp://path/goes/here/1.js")
}
这种方法的好处是它将基本属性约束为当前类型或更多派生,而不是更少派生。
<小时/> 替代方法:显式解析而不是implicit
运算符
如果您需要将LocalPath
和FtpPath
变量保留为ResourceBase
,或者在此处不能使用泛型,则隐式运算符将开始变得混乱。最好提供像静态方法一样的显式内容:
new JavaScript()
{
LocalPath = JavaScript.Parse("/path/goes/here/1.js"),
FtpPath = JavaScript.Parse("ftp://path/goes/here/1.js")
}
class JavaScript
{
public static ResourceBase Parse(string s)
{
if (path.EndsWith(".js"))
return new JavaScript(path);
throw new Exception();
}
}
<小时/> 替代方法:类层次结构解析而不是
implicit
运算符
通过构造函数将字符串使用到类型中,并将属性设置为只读:
public abstract class ResourceBase
{ }
public abstract class WebResourceBase
{
public ResourceBase LocalPath { get; private set; }
public ResourceBase FtpPath { get; private set; }
protected abstract ResourceBase ParseLocalPath(string s);
protected abstract ResourceBase ParseFtpPath(string s);
}
public class JavaScript : WebResourceBase<JavaScript>
{
protected override ResourceBase ParseLocalPath(string s)
{
// etc.
}
protected override ResourceBase ParseFtpPath(string s)
{
// etc.
}
}
<小时/> 说实话,大多数这似乎有点过分,只是为了从字符串中将两个属性设置为特定类型,你有很多选项 - 即使是隐式运算符也可以。
选择最容易理解的那个。除非你去挖掘它,否则运算符重载往往会有些隐藏。
答案 1 :(得分:1)
我也假设WebResourceBase
应该从ResourceBase
继承
在基类上有一个protected
映射机制,派生类可以自己订阅:
public abstract class ResourceBase
{
// Records how to make a ResourceBase from a string,
// on a per-extension basis
private static Dictionary<string, Func<string, ResourceBase>> constructorMap
= new Dictionary<string, Func<string, ResourceBase>>();
// Allows a derived type to subscribe itself
protected static void Subscribe(
string extension,
Func<string, ResourceBase> ctor)
{
if (constructorMap.ContainsKey(extension))
throw new Exception("nuh uh");
constructorMap.Add(extension, ctor);
}
// Given a string, finds out who has signed up to deal with it,
// and has them deal with it
public static implicit operator ResourceBase(string s)
{
// Find a matching extension
var matches = constructorMap.Where(kvp => s.EndsWith(kvp.Key)).ToList();
switch (matches.Count)
{
case 0:
throw new Exception(
string.Format("Don't know how to make {0} into a ResourceBase",
s));
case 1:
return matches.Single().Value(s);
default:
throw new Exception(string.Format(
"More than one possibility for making {0} into a ResourceBase",
s));
}
}
}
中间类型基本没有变化,但是在某些类型检查中我无法确定如何在编译时强制执行:
public abstract class WebResourceBase : ResourceBase
{
private ResourceBase localPath;
public ResourceBase LocalPath
{
get { return localPath; }
set
{
if (value.GetType() != GetType())
{
throw new Exception("Naughty");
}
localPath = value;
}
}
private ResourceBase ftpPath;
public ResourceBase FtpPath
{
get { return ftpPath; }
set
{
if (value.GetType() != GetType())
{
throw new Exception("Naughty");
}
ftpPath = value;
}
}
}
具体类型如下:
public class JavaScript : WebResourceBase
{
public JavaScript()
{
}
private JavaScript(string s)
{
}
static JavaScript()
{
Subscribe("js", s => (ResourceBase)new JavaScript(s));
}
}
用法如您所指定:
var js = new JavaScript
{
LocalPath = "hello.js",
FtpPath = "hello.js"
};
请注意,尽管ResourceBase
和constructorMap
的签名中有Subscribe
,但在上述语句LocalPath
和FtpPath
之后> JavaScript
个对象。
答案 2 :(得分:0)
我只想在流程中添加一个额外的间接层(这对于设计问题而言,这是一个很好的答案,这真是令人惊讶;))。
我要么添加一个新的构造函数,它接受两个字符串参数,添加两个额外的字符串属性,或两者兼而有之。 (我假设您只是从这里添加两个新的字符串属性;您可以从那里进行推断。)
如果你添加一个新属性:JavaScriptLocalPath
它可以在它的set方法中将字符串转换为特定于ResourceBase
的派生类型JavaScript
,并设置为{{1使用该结果的属性。我假设它的get方法也可以从LocationPath
中提取该字符串(这样你也不需要费心存储ResourceBase
)。
答案 3 :(得分:0)
这是我的想法:
public abstract class WebResourceBase {
public ResourceBase LocalPath { get; set; }
public ResourceBase FtpPath { get; set; }
protected abstract ResourceBase ConvertFromString(string path);
public string LocalPathStr { set { LocalPath = ConvertFromString(value); } }
public string FtpPathStr { set { FtpPath = ConvertFromString(value); } }
}
可能会有所改进。