3
\$\begingroup\$

I have complex logic manage EnvDTE.Project and EnvDTE.ProjectItem for a VS Addin (VS 2010).

I would like elegant, easily maintainable code that follows DRY and KISS principles. Maybe using helper with Action<T> or Func<T> methods.

I want a Dictionary<string, ItemBranchable>, for access by "Key" (an Id, the name of a projectItem).

And now I have 5 lists: List<ItemBranchable>.

Maybe for more performance I could use 5 lists or one list for all, and entity ItemBranchable will have a Type property, and using Linq to filter it by Type.

Full source code:

  public partial class ModelBranchingMerging
    {
        private static ModelBranchingMerging Instance = null;

        public static ModelBranchingMerging GetModel(Project project)
        {
            if (Instance == null)
            {
                Instance = new ModelBranchingMerging();
                Instance.NavigateProject(project);
            }
            return Instance;
        }

        private ModelBranchingMerging()
        {
            Data = new Dictionary<string, ItemBranchable>();
            Forms = new List<ItemBranchable>();
            Packages = new List<ItemBranchable>();
            PackagesBodies = new List<ItemBranchable>();
            Types = new List<ItemBranchable>();
            TypesBodies = new List<ItemBranchable>();
        }

        public Dictionary<string, ItemBranchable> Data { get; set; }
        public List<ItemBranchable> Forms { get; set; }
        public List<ItemBranchable> Packages { get; set; }
        public List<ItemBranchable> PackagesBodies { get; set; }
        public List<ItemBranchable> Types { get; set; }
        public List<ItemBranchable> TypesBodies { get; set; }

       // *** OMMITTED ***
        private void NavigateProjectItems(ProjectItems colProjectItems)
        {
            if (colProjectItems == null) return;

            foreach (EnvDTE.ProjectItem objProjectItem in colProjectItems)
            {
                if ((objProjectItem.Collection.Parent as ProjectItem) != null)
                {
                    var parentName = ((ProjectItem)objProjectItem.Collection.Parent).Name;

                    var itemBranchable = new ItemBranchable(objProjectItem);

                    if (parentName == Operaciones.FolderForms) { Forms.Add(itemBranchable); Data.Add(itemBranchable.Id, itemBranchable); }
                    if (parentName == Operaciones.FolderPackages) { Packages.Add(itemBranchable); Data.Add(itemBranchable.Id, itemBranchable); }
                    if (parentName == Operaciones.FolderPackages_Bodies) 
                    { 
                        PackagesBodies.Add(itemBranchable); 
                        Data.Add(itemBranchable.Id, itemBranchable); 
                    }
                    if (parentName == Operaciones.FolderTypes) { Types.Add(itemBranchable); Data.Add(itemBranchable.Id, itemBranchable); }
                    if (parentName == Operaciones.FolderTypes_Bodies) { TypesBodies.Add(itemBranchable); Data.Add(itemBranchable.Id, itemBranchable); }

                }


                if (objProjectItem.SubProject != null)
                {
                    NavigateProject(objProjectItem.SubProject);
                }
                else
                {
                    NavigateProjectItems(objProjectItem.ProjectItems);
                }

            }
        }

    }

And ItemBranchable class:

  public class ItemBranchable
    {
        public string Id { get; set; }
        public string Parent { get; set; }

        public bool IsBranched { get; set; }
        public List<ItemBranchable> Branchs { get; set; }

        public bool IsBranch { get; set; }
        public string IdSourceBranched { get; set; } // Id in Branchs list?

        public List<ItemBranchable> BranchsUsed { get; set; }

        internal ItemBranchable(EnvDTE.ProjectItem item)
        {
            Id = item.Name;
            Parent = ((ProjectItem)item.Collection.Parent).Name;

            // TODO: Read Metadata from EnvDTE.Project ...
        }
    }

Any suggestions ?

\$\endgroup\$
2
\$\begingroup\$

You almost gave the answer by yourself:

Maybe for more performance I could use 5 lists or one list for all, and entity ItemBranchable will have a Type property, and using Linq to filter it by Type.

Everytime I need to separate a collection of objects I insert them in the same list and then filter them by type. You can also put them in a dictionary, that is better if you are filtering the objects by type many times. Although sometimes the best thing you can do is to have a second look in your code, in most cases there is a design pattern that you could apply and you wouldn't have this issue in first place.

private ModelBranchingMerging()
{
    Data = new List<ItemBranchable>();
}

public ICollection<ItemBranchable> Data { get; set; }
public IEnumerable<ItemBranchable> Forms 
    get{
        return Data.Where(d => d.Parent == Operaciones.FolderForms);
    }
}
//so on so forth

//and your NavigateProjectItems implementation:

private void NavigateProjectItems(ProjectItems colProjectItems)
    {
        if (colProjectItems == null) return;

        foreach (EnvDTE.ProjectItem objProjectItem in colProjectItems)
        {
            var parentProjectItem = (objProjectItem.Collection.Parent as ProjectItem);
            if (parentProjectItem != null)
            {
                var parentName = parentProjectItem.Name;
                var itemBranchable = new ItemBranchable(parentProjectItem);
                Data.Add(itemBranchable); 
            }

            if (objProjectItem.SubProject != null)
            {
                NavigateProject(objProjectItem.SubProject);
            }
            else
            {
                NavigateProjectItems(objProjectItem.ProjectItems);
            }

        }
    }

Notice that I had your foreach body a bit modified, I'll be stating some good practices that you should follow:

  • If you need to perform a cast, do so on a separate statement with the as operator (that you already used).
  • If a cast is made always use the variable with the most specific type in the remaining implementation (if applicable, in this case you can't to this to call the method recursively).

Like I said you could also make use of a dictionary, it's pretty easy to make a to group the items by type with linq:

public class KeyValue{
    public int Key{get;set;}
    public string Value{get;set;}
}

void Main()
{
    List<KeyValue> list = new List<KeyValue>();
    list.Add(new KeyValue(){Key = 1, Value = "1.1"});
    list.Add(new KeyValue(){Key = 1, Value = "1.2"});
    list.Add(new KeyValue(){Key = 1, Value = "1.3"});
    list.Add(new KeyValue(){Key = 2, Value = "2.1"});
    list.Add(new KeyValue(){Key = 2, Value = "2.2"});
    IDictionary<int, List<KeyValue>> map = list
        .GroupBy(item => item.Key)
        .ToDictionary(g => g.Key, v => v.ToList());
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.