在LINQ查询中转换为子类型

时间:2017-01-20 01:47:21

标签: c# linq

我已经回顾了类似问题的其他答案,但无法找到我想要的内容。我有一个Superclass类型的字典,可以存储2个子类中的任何一个的实例。我需要能够访问其中一个子类的成员。我无法弄清楚如何将LINQ查询转换为子类来访问它。这就是我所拥有的。

class VirtualFabric
{  Dictionary<string, SANSwitch} MemberSwitches = new Dictionary<string, SANSwitch>();    

class SANSwitch
{  
     string SwitchWWN {get; set; }
     Dictionary<int, VirtualFabric> VirtualFabrics = new Dictionary<int, VirtualFabric>();
}

class CiscoSwitch : SanSwitch {}
class BrocadeSwitch : SanSwitch
{
 public Dictionary<int, string> VirtualWWNList = new Dictionary<int, string>();

  public bool HasWWN(string wwn)
     {
        if (wwn.StartsWith("55")) { return this.VirtualWWNList.Values.Contains(wwn); }
        else { return this.SwitchWWPN.Contains(wwn); }
     }    
}

因此,VirtualFabric中的MemberSwitches可以存储CiscoSwitch或BrocadeSwitch的实例。我试图这样做:

List<SwitchPort> isls = this.ISLs.SelectMany(p => p.Value.Where(i => i is FCPort)).ToList();

        // set up the ISLs
        // if the remote switch port list already contains a port, then that switch log has been processed 
        // so do nothing

 foreach (SwitchPort p in isls)
{   
    var bs = this.VirtualFabricList.SelectMany(t =>   t.Value.MemberSwitches.Values.Where
(s => s.Value.HasWWN(p.RemoteSwitchWWPN)));  

// do something with any results
} // foreach

SelectMany未编译,因为HasWWN()是在BrocadeSwitch中定义的,而不是在SANSwitch中定义的。我可以把它移到SANSwitch,但不想。我也可以在Brocade类型的MemberSwitches中获取所有开关或迭代并将它们声明为Brocade然后搜索该列表,但我想知道是否有一种方法可以在LINQ语句中进行转换。我见过选择某种类型的例子,但这不是我想要的。

2 个答案:

答案 0 :(得分:0)

这是一个派生另一个类的类:

public class Class1 { }

public class Class2 : Class1 {
   public List<Class2> Twos { get { return new List<Class2>(); } }
}

以下是如何施放:

var c1 = new Class2();
var c2 = new Class2();
var l = new List<Class2> { c1, c2 };
var ints = l.SelectMany( x => x.Twos ).Cast<Class1>();

或者只是投射枚举器而不是像这样投射每个对象:

var ints = l.SelectMany( x => x.Twos ) as IEnumerable<Class1>;

由于Class2派生Class1,您可以进行投射。但是,如果您尝试从Class1投射到Class2,则会失败,因为Class2更具体。

修改

你有这个lambda:

var bs = this.VirtualFabricList.SelectMany(t =>   t.Value.MemberSwitches.Values.Where
(s => s.Value.HasWWN(p.RemoteSwitchWWPN))); 

MemberSwithches.Values将返回SANSwitch,然后您尝试调用s.Value.HasWWN方法,但CiscoSwitch没有HasWWN方法。你不能这样做。你需要这样做:

var bs = this.VirtualFabricList.SelectMany( t => t.Value.MemberSwitches.Values
   .Where( s => s.Value is BrodcadeSwitch ) ).Cast<BrodcadeSwitch>()
   .Where( s => s.HasWWN( p.RemoteSwitchWWPN ) );

答案 1 :(得分:0)

您忘了描述VirtualFabricList类型。

从你的linq语句我收集到一个VirtualFabricList不是VirtualFabric对象的序列,因为VirtualFabricList中的每个“t”都有一个属性Value,这个属性不在你的VirtualFabric类中。

然而,从你的其他linq我收集到每个t.Value是一个VirtualFabric。此外,每个t.Value都有一个属性MemberSwitches,它是一个包含SANSwitch对象的字典。

您只需要此字典中的某些对象:只有字典中的那些SANSwitch是BrocadeSwitch对象。甚至不是所有的BrocadeSwitch,只有HasWwn(...)返回true的那些。

您的问题是您检查了SANSwitches字典中HasWWN的所有元素,而您只能检查BrocadeSwitch字符。为此,您可以使用Enumerable.OfType

小步骤:

IEnumerable<VirtualFabric> virtualFabrics = this.VirtualFabricList
    .Select(t => t.Value>);
IEnumerable<Dictionary<string, SANSwitch>> dictionaries = virtualFabrics
    .Select(fabric => fabric.MemberSwitches);
IEnumerable<SanSwitch> sanSwitches = dictionaries
    .SelectMany(dictionary => dictionary.Values);

注意:如果我对您的VirtualFacbricList的猜测不正确,请使用您自己的语句来获取一系列SanSwitch对象。从这个序列中你可以得到BrocadeSwitches:

IEnumerable<SanSwitch> sanSwitches = ...
IEnumerable<BrocadeSwitch> brocadeSwitches = sanSwitches.OfType<BrocadeSwitch>();
IEnumerable<BrocadeSwitch> switchesWithWWN = brocadeSwitches
    .Select(brocadeSwitch => brocadeSwitch.HasWWN(...)

所以关键功能是 Enumerable.OfType

这些小步骤可帮助您检查每个linq语句是否包含您期望的项目。它不会减慢你的进程,因为延迟执行这些语句直到你的ToList()才会被执行。

当然,如果可读性和调试能力不是优先级列表中最高的,那么您可以将这些语句放入一个大的linq语句中。