我正在尝试使用类型为T的linq表达式创建一个对象类型(MyObject)。我的类声明T的值必须是BaseModel类型(这是我创建的对象)。下面是MyObject的构造方式:
public class MyObject<T> where T : BaseModel
{
public Type MyType;
public Expression<Func<T, bool>> MyExpression;
}
我的所有模型都继承自BaseModel。例如:
public class MyModel : BaseModel
{
public string Name { get; set; }
}
我的对象在通用静态类中使用:
public static class MyStaticClass
{
private static Dictionary<MyObject<BaseModel>, string> MyDictionary = new Dictionary<MyObject<BaseModel>, string>();
public static AddMyObjectsToDictionary(List<MyObject<BaseModel>> myObjects)
{
//CODE
}
//REST OF CODE
}
然后,当我的应用加载时,会执行以下(此处引发错误):
List<MyObject<BaseModel>> myObjects = new List<MyObject<BaseModel>>();
myObjects.Add(new MyObject<MyModel>()
{
MyType = typeof(MyModel),
MyExpression = p => p.Name == "myname"
});
MyStaticClass.AddMyObjectsToDictionary(myObjects);
使用命名空间抛出完全错误消息,以显示每个对象所在的项目:
无法从
'ProjectANamespace.MyObject<ProjectBNamespace.MyModel>'
转换为'ProjectANamespace.MyObject<ProjectANamespace.BaseModel>'
我需要能够在MyModel中创建一个通用表达式,但是我不能在MyStaticClass中指定MyModel,因为它是一个泛型类,它与BaseModel一起位于另一个项目中。
任何人都有任何想法如何解决这个问题?
答案 0 :(得分:4)
MyObject<MyModel>
不是MyObject<BaseModel>
,就像List<string>
不是List<object>
一样。它在泛型之外的工作原理相同 - 一般概念称为 variance ,并描述了类型的有效替换。由于MyObject
既不是T
的共变体也不是反变体,MyObject<T>
永远不会是MyObject<U>
,因为任何不同的T和U都是
接口和委托中的泛型类型参数既可以是共变量也可以是反变体,但不能同时兼具。例如,您的Func<T, bool>
是反式变体,因为您可以替换从T
而非T
本身派生的类型。因此,Func<BaseModel, bool>
可以转换为Func<MyModel, bool>
(正如MyModel
可以作为参数“而不是BaseModel
)传递,但反之亦然。类似地,Func<T, U>
相对于T
和U
的共变体是反变量的 - 您不能从具有返回类型的函数返回object
string
。
因此,即使您将定义更改为使用界面添加方差,您也只能获得反差异,而不是您想要的协方差。太糟糕了 - 没有安全的方法可以做到这一点。
相反,如果您需要非通用接口,只需添加非通用接口即可。而已。而不是List<MyObject<BaseModel>>
,您将拥有List<IMyObject>
,您可以根据需要使用Expression
(可能通过投射,或仅展示简单的Expression<Func<T, bool>>
而不是OrderedDict
)。
答案 1 :(得分:0)
要做到这一点,我相信你需要使用c#4+的Covariance功能。特别是,我认为您需要使用out修饰符。经过一些测试,我发现这不适用于课程。但是,如果在模式中引入接口,则可以使其工作。例如,以下编译
var root = { name: "root" };
var hosts = [
{ name: "host1", type: "host" },
{ name: "host2", type: "host" },
{ name: "host3", type: "host" }
];
var nodes = [root].concat(hosts);
var links = hosts.map(function(host) {
return { source: root, target: host }
});
hosts.forEach(function(host) {
var hostNum = host.name.substr(4);
for(var i = 0; i <= 5; i++) {
var vm = {
name: "vm-" + hostNum + "-" + i,
type: 'vm'
}
nodes.push(vm);
links.push({
source: host,
target: vm
})
}
});
var force = d3.layout.force()
.size([window.innerWidth, window.innerHeight])
.nodes(nodes)
.links(links)
.charge(-1500)
.gravity(0.1)
.on('tick', update)
.start();
var svg = d3.select('body')
.append('svg')
.attr({
width: window.innerWidth,
height: window.innerHeight
})
var circles = svg.selectAll('circle')
.data(force.nodes())
circles.enter()
.append('circle')
.attr({
r: function(d) { return d.type == 'vm' ? 14 : 20; },
fill: '#1661FE'
});
var lines = svg.selectAll('line')
.data(force.links())
lines.enter()
.append('line')
.attr({
fill: 'none',
stroke: '#1661FE',
'stroke-width': 3
});
var texts = svg.selectAll('text')
.data(force.nodes())
texts.enter()
.append('text')
.text(function(d) { return d.name; })
.attr({
fill: 'white',
'text-anchor': 'middle',
dy: '30'
})
.style({
'font-family': "Verdana, Helvetica, Sans-Serif",
'font-size': 12
});
function update() {
circles.attr({
cx: function(d) { return d.x; },
cy: function(d) { return d.y; }
});
texts.attr({
x: function(d) { return d.x; },
y: function(d) { return d.y; }
})
lines.attr({
x1: function(d) { return d.source.x},
y1: function(d) { return d.source.y},
x2: function(d) { return d.target.x},
y2: function(d) { return d.target.y},
})
}