0

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.

Community
  • 1
  • 1
Carl Onager
  • 4,112
  • 2
  • 38
  • 66

2 Answers2

3

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();
Rahul Singh
  • 21,585
  • 6
  • 41
  • 56
  • 1
    This is exactly what VB is (strangely) doing behind the scenes when you use indexing on an IEnumerable object. See Jon Skeet's reply in this question: http://stackoverflow.com/questions/15864783/why-can-i-apply-an-indexer-to-an-icollection-in-vb-net-but-not-in-c-sharp – Dave Doknjas Mar 10 '15 at 18:53
  • @DaveDoknjas If you type that up as an answer I will mark it as the correct one – Carl Onager Mar 11 '15 at 15:10
  • @ClaraOnager: No - the answer belongs to Rahul - the C# equivalent is to use "ElementAtOrDefault(0)" since this is exactly what VB is doing behind the scenes. – Dave Doknjas Mar 11 '15 at 17:22
1

You can just use First method:

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

Or use FirstOrDefault and check for null if there is a chance query doesn't return any results.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
  • That results in a `Cannot assign method group to an implicitly-typed local variable` error instead. – Carl Onager Mar 10 '15 at 13:14
  • 1
    @Clara Double check it's `.First()` and not `.First`. http://stackoverflow.com/questions/19623299/cannot-assign-method-group-to-an-implicitly-typed-local-variable – hometoast Mar 10 '15 at 13:19
  • @hometoast Definitely `.First()`. I think that for some reason I'm not entirely sure about the Linq query returns an array of IEnumerable and thus you need to have select the first of first element. – Carl Onager Mar 10 '15 at 13:22
  • **This answer is incorrect**, 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 for the reason why here: http://stackoverflow.com/questions/15864783/why-can-i-apply-an-indexer-to-an-icollection-in-vb-net-but-not-in-c-sharp – Carl Onager Mar 23 '15 at 15:08
  • @ClaraOnager - That's fine to state in a comment, and to vote based on that, but editing your commentary into someone else's answer is not appropriate. Please don't do that again. – Brad Larson Mar 23 '15 at 15:16
  • @Selman22 Show some working code that demonstrates your answer as correct and I will retract my statement. – Carl Onager Mar 24 '15 at 10:08