In C# 4.0, is there any way to make an otherwise private member of one class available only to a specific other class?

前端 未结 9 1879
旧时难觅i
旧时难觅i 2021-01-31 19:27

We\'re creating an object hierarchy where each item has a collection of other items, and each item also has a Parent property pointing to its parent item. Pretty st

相关标签:
9条回答
  • 2021-01-31 19:44

    My answer is built off of two parts

    1. Why is it so "Unsafe" to have the Interface ISetParent?

    private/internal access modifiers are ment to prevent from mistakes, not really "Secure Code".

    Remember... you can call private methods using some Reflections/Invoke Etc...

    .

    2 . i usually make everything public and make sure both sides know how to handle each other,

    ofcourse, there is a little ping-pong but it takes just few cycles (in this case i have a NamedSet)

        private IPhysicalObject partOf;
        public IPhysicalObject PartOf
        {
            get { return partOf; }
            set
            {
                if (partOf != value)
                {
                    if (partOf != null)
                        partOf.Children.Remove(this.Designation);
    
                    partOf = value;
    
                    if (partOf != null)
                        partOf.Children.Add(this.Designation);
                }
            }
        }
    
        public virtual void Add(String key, IPhysicalObject value)
        {
            IPhysicalObject o;
            if (!TryGetValue(key, out o))
            {
                innerDictionary.Add(key, value);
                value.PartOf = Parent;
            }
        }
    
        public virtual bool Remove(String key)
        {
            IPhysicalObject o;
            if(TryGetValue(key, out o))
            {
                innerDictionary.Remove(key);
                o.PartOf = null;
            }
        }
    

    Hope this helps... Good Day, Tomer W.

    P.S. I'll never get how this Editor is working... should i HTML it, or should i not?

    0 讨论(0)
  • 2021-01-31 19:45

    Here's a way you can simulate friend in C#:

    Mark your properties internal and then use this attribute to expose them to friend assemblies:

    [assembly: InternalsVisibleTo("Friend1, PublicKey=002400000480000094" + 
                                  "0000000602000000240000525341310004000" +
                                  "001000100bf8c25fcd44838d87e245ab35bf7" +
                                  "3ba2615707feea295709559b3de903fb95a93" +
                                  "3d2729967c3184a97d7b84c7547cd87e435b5" +
                                  "6bdf8621bcb62b59c00c88bd83aa62c4fcdd4" +
                                  "712da72eec2533dc00f8529c3a0bbb4103282" +
                                  "f0d894d5f34e9f0103c473dce9f4b457a5dee" +
                                  "fd8f920d8681ed6dfcb0a81e96bd9b176525a" +
                                  "26e0b3")]
    
    public class MyClass {
       // code...
    }
    
    0 讨论(0)
  • 2021-01-31 19:45

    The first thing that struck me with this scenario is that there is definite feature envy between ItemCollection and Item. I understand your desire to make adding the child item to the collection and setting the parent to be an autonomous operation, but really I think the responsibility of maintaining that relationship is in the Item, not the ItemCollection.

    I would recommend exposing the ChildItems on Item as a Read-Only collection (with IEnumerable<Item> perhaps), and putting the AddChild(Item child),RemoveChild(Item child), ClearChildren(), etc methods on the Item. That puts the responsibility for maintaining the Parent with the Item where you don't have concerns leaking into other classes.

    0 讨论(0)
  • 2021-01-31 19:55

    The only two things I can think of:

    One:

    Use sort of option number 2 you mention above (which I do constantly myself)...but make the implementation of the interface (Item) be a nested private class inside of ItemsCollection... that way only ItemsCollection knows about the setter. The interface IItem only declares a getter for Parent...and no one can cast it to Item because Item is private to ItemsCollection. So, something like:

    public class ItemsCollection : ObservableCollection<IItem>
    {
        private class Item : IItem 
        {
            public object Parent { get; set; }
        }
    
        private CheckParent(IItem item)
        {
            if(item.Parent != null) throw new Exception("Item already belongs to another ItemsCollection");
            ((Item)item).Parent = this.Owner; // <-- This is where we need to access the private Parent setter
        }
    
        public static IItem CreateItem() { return new Item(); }
    }
    
    public interface IItem 
    {
        object Parent {get; }
    }
    

    and when you want ItemsCollection to set the item Parent, case the IItem instance to Item (which does expose a setter). Only ItemsCollection can do this cast, since the Item implementation is private to ItemsCollection...so I think that accomplishes what you want.

    Two:

    Make it internal not private...you don't get exactly what you want, but you can use InternalsVisibleToAttribute to denote that the internal members are visible to another assembly.

    0 讨论(0)
  • 2021-01-31 19:56

    I have to solve your problem every day, but I don't do it the way you're trying to do it.

    Take a step back. What is the fundamental problem you're trying to solve? Consistency. You are trying to ensure that the "x is a child of y" relationship and the "y is the parent of x" relationship are always consistent. That's a sensible goal.

    Your supposition is that every item directly knows both its children and its parent because the children collection and the parent reference are stored locally in fields. This logically requires that when item x becomes a child of item y, you have to consistently change both x.Parent and y.Children. That presents the problem that you've run into: who gets to be "in charge" of making sure that both changes are made consistently? And how do you ensure that only the "in charge" code gets to mutate the parent field?

    Tricky.

    Suppose we denied your supposition. It need not be the case that every item knows both its children and its parent.

    Technique #1:

    For example, you could say that there is one special item called "the universe", which is the ancestor of every item except itself. "The universe" could be a singleton stored in a well-known location. When you ask an item for its parent, the implementation could find the universe, and then do a search of every descendant of the universe looking for the item, keeping track of the path as you go. When you find the item, great, you're done. You look one step back on the "path" that got you there, and you have the parent. Even better, you can provide the entire chain of parents if you want; after all, you just computed it.

    Technique #2:

    That could be expensive if the universe is large and it takes a while to find each item. Another solution would be to have the universe contain a hash table that maps items to their parents, and a second hash table that maps items to a list of their children. When you add child x to parent y, the "add" method actually calls the Universe and says "hey, item x is now parented by y", and the Universe takes care of updating the hash tables. Items do not contain any of their own "connectedness" information; that's the responsibility of the universe to enforce.

    A down side of that is it is possible for the universe to then contain cycles; you could tell the universe that x is parented by y and y is parented by x. If you wish to avoid this then you'd have to write a cycle detector.

    Technique #3:

    You could say that there are two trees; the "real" tree and the "facade" tree. The real tree is immutable and persistent. In the real tree, every item knows its children but not its parent. Once you have built the immutable real tree, you make a facade node that is a proxy to the root of the real tree. When you ask that node for its children, it makes a new facade node wrapped around each child and sets the parent property of the facade node to the node that was queried for its children.

    Now you can treat the facade tree as a parented tree, but the parent relationships are only computed as you traverse the tree.

    When you want to edit the tree, you produce a new real tree, re-using as much of the old real tree as possible. You then make a new facade root.

    The downside of this approach is that it only works if you typically traverse the tree from the top down after every edit.

    We use this latter approach in the C# and VB compilers because that is precisely the situation we are in: when we rebuild a parse tree after a code edit we can re-use much of the existing immutable parse tree from the previous text. We always traverse the tree from the top down, and only want to compute the parent references when necessary.

    0 讨论(0)
  • 2021-01-31 19:58

    C# Doesn't have a friend keyword, but it has something close called internal. You could mark the methods you want to expose in a limited fashion as internal and then only other types in that assembly will be able to see them. If all your code is in one assembly, this won't help you much, but if this class is packaged in a separate assembly it would work.

    public class Item
    {
        public string Name{ get; set; }
        public Item Parent{ get; internal set; } // changed to internal...
        public ItemsCollection ChildItems;
    
        public Item()
        {
            this.ChildItems = new ItemsCollection (this);
        }
    }
    
    0 讨论(0)
提交回复
热议问题