开发者

Is it possible to initialize an object in a class constructor using LINQ?

Am I thinking about this incorrectly?? 开发者_StackOverflow中文版Or missing something completely obvious? I can best show by example. This code is how I initialize a new a new object now. It seems redundant.

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    public Person(int Id)
    {
        XDocument personXml = XDocument.Load("person.xml");
        var person = (from p in personXml.Descendants("Person")
                      where (int)p.Attribute("id") == Id
                      select new
                      {
                          FirstName = (string)p.Element("FirstName"),
                          LastName = (string)p.Element("LastName"),
                          Age = (int)p.Element("Age")
                      }).SingleOrDefault();

        //with the "select new" in query I have to set the properties manually
        FirstName = person.FirstName;
        LastName = person.LastName;
        Age = person.Age;
    }
}

And this is what I'm trying to do but can't get it to work:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    public Person(int Id)
    {
        XDocument personXml = XDocument.Load("person.xml");
        (from p in personXml.Descendants("Person")
         where (int)p.Attribute("id") == Id
         select
         {
             FirstName = (string)p.Element("FirstName"),
             LastName = (string)p.Element("LastName"),
             Age = (int)p.Element("Age")
         }).SingleOrDefault();
    }
}

I feel like there should be a way to accomplish what I am trying to do. I must be missing some syntactic or fundemetal understanding of why it doesn't. Or maybe I'm simply crazy for thinking it should be done this way.


This is not the purpose of LINQ. what you have there is a query projection which might have hundreds of rows coming from it. It wouldn't make sense to set values external to the query there because only the last would stay.

However, you could do something like this:

public static Person CreatePerson(int Id)
{
    return (from p in XDocument.Load("person.xml").Descendants("Person")
            where (int)p.Attribute("id") == Id
            select new Person
                   {
                       FirstName = (string)p.Element("FirstName"),
                       LastName = (string)p.Element("LastName"),
                       Age = (int)p.Element("Age")
                   }).SingleOrDefault();
}

Further important note: In general it is a bad idea to be doing I/O and other expensive (and possibly failure prone) work within constructors. You can often avoid problems by keeping constructors short and loading data in factory methods like this one.


You need to make it a static method and create a new Person object like this:

    public static Person LoadPerson (int Id)
    {
        XDocument personXml = XDocument.Load("person.xml");
        var person = (from p in personXml.Descendants("Person")
                      where (int)p.Attribute("id") == Id
                      select new Person
                      {
                          FirstName = (string)p.Element("FirstName"),
                          LastName = (string)p.Element("LastName"),
                          Age = (int)p.Element("Age")
                      }).SingleOrDefault();

    }


Never write "dangerous code" in the constructor. The behavior you try to load an XML to build an instance in the constructor is considered to be "dangerous" because the code probably throw exceptions (it's not recommended even if you add a try-catch block). It seems that you want to implement the Factory Pattern. So you need a PersonManager which is like a factory to produce Person instances to others with a static method.

public class PersonManager
{
   public static Person GetPerson(int id)
   {
      //get the Person here
   }
}

and your Person class should be very simple:

public class Person
{
   //others can only get Person instances from your PersonManager
   internal Person() { }   

   public string FristName { get; set; }
   public string LastName { get; set; }

   // and some other properties/methods which are really related to a Person
}


Your first version is correct and not redundant. When you call Select(new...) you create anonymous type, and don't initialize yours... and it's also lazy - the anonymous type properties are not initialized until you actually use them.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜