Search code examples
c#vb.netlinqreflectioncode-translation

Correctly Translate .Take(1)(0) to C# to fix Cannot apply indexing with [] error?


I have a bit of VB code that needs to be translated into C#:

<XmlIgnore> Private myState As Object = Me
''' <summary>
''' Fill out the properties of this class by deserializing the given XML into an object of the same type as this class and then copying it's properties.
''' </summary>
''' <param name="xml">String</param>
''' <remarks>This is a shallow copy so won't read any nested objects.</remarks>
Public Sub Deserialize(xml As String)
    Dim newMe As Object 'We don't know our own type so we have to use an object here
    'Read text
    Dim newSerializer As New XmlSerializer(Me.GetType)
    Using newReader As New StringReader(xml)
        newMe = newSerializer.Deserialize(newReader)
        myState = newMe
    End Using
    For Each propinfo In myState.GetType.GetProperties()
        Dim name = propinfo.Name
        Dim realProp = (From p In Me.GetType.GetProperties
          Where p.Name = name And p.MemberType = Reflection.MemberTypes.Property).Take(1)(0)
        If realProp.CanWrite = True Then realProp.SetValue(Me, propinfo.GetValue(myState, Nothing), Nothing)
    Next
End Sub

Using a tool it has come out like this:

[XmlIgnore] private object myState;
/// <summary>
/// Fill out the properties of this class by deserializing the given XML into an object of the same type as this class and then copying it's properties.
/// </summary>
/// <param name="xml">String</param>
/// <remarks>This is a shallow copy so won't read any nested objects.</remarks>
public void Deserialize(string xml)
{
    object newMe = null; //We don't know our own type so we have to use an object here
    //Read text
    XmlSerializer newSerializer = new XmlSerializer(this.GetType());
    using (StringReader newReader = new StringReader(xml))
    {
        newMe = newSerializer.Deserialize(newReader);
        myState = newMe;
    }
    foreach (var propinfo in myState.GetType().GetProperties())
    {
        var name = propinfo.Name;
        var realProp = (
            from p in this.GetType().GetProperties()
            where p.Name == name && p.MemberType == System.Reflection.MemberTypes.Property
            select p).Take(1)[0];
        if (realProp.CanWrite == true)
        {
            realProp.SetValue(this, propinfo.GetValue(myState, null), null);
        }
    }
}

However the compiler is complaining about the .Take(1)[0] line with the following error: Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.IEnumerable<System.Reflection.PropertyInfo>'

I think that using .Take(1).ElementAt(0) may be correct but how can I definitively alter the .Take to correctly take the first element of the first item as per the intent of the VB code?

Postscript

Note that using .First results in an Cannot assign method group to an implicitly-typed local variable error. Instead .ElementAtOrDefault must be used to get the correct result. See Jon Skeet's answer here for the reason why.


Solution

  • Your equivalent code in C# would be:-

    var realProp = (from p in this.GetType().GetProperties()
            where p.Name == name && p.MemberType == System.Reflection.MemberTypes.Property
            select p).Take(1).ElementAtOrDefault(0);
    

    Here, in ElementAtOrDefault you need to pass the index of the element you want to fetch.

    If you are interested in fetching only the first element then you can use FirstOrDefault :-

    var realProp = (from p in this.GetType().GetProperties()
               where p.Name == name && p.MemberType == System.Reflection.MemberTypes.Property
                select p).FirstOrDefault();