Add friend class in c# #2073
Replies: 85 comments 78 replies
-
Why can't you just nest one class in another? |
Beta Was this translation helpful? Give feedback.
-
I'd suggest that nested classes are the appropriate way to express that relationship, especially since the CLR supports providing access to the private members. The CLR doesn't support any special relationship beyond that point so the best you can do is have the compiler enforce custom rules based on |
Beta Was this translation helpful? Give feedback.
-
If I could it in an easy way, I wouldn't ask for it. Having a feature is always nice. |
Beta Was this translation helpful? Give feedback.
-
InternalsVisibleToAttribute is your best best. |
Beta Was this translation helpful? Give feedback.
-
but they are at assembly level, I want it in class level |
Beta Was this translation helpful? Give feedback.
-
Then maybe you either need to refactor code into separate assemblies, or ask yourself who/what you're trying to protect some classes from but not others. |
Beta Was this translation helpful? Give feedback.
-
It's worth exploring, related: #2062. |
Beta Was this translation helpful? Give feedback.
-
@Muziburrahman class A
{
private int _x;
public class B
{
public B() {
var x = new A()._x;
}
}
} From class |
Beta Was this translation helpful? Give feedback.
-
@ichensky thanks for your suggestion. |
Beta Was this translation helpful? Give feedback.
-
Nested classes increase the file size and number of lines inside a single class. This is a typical situation: you create a folder for a feature and all feature-related classes are in it. Some of the classes are supposed to be used only within the feature (i.e., other classes under the same folder) and not from outside, while only a few of the classes are exposed to the outside world. Just like modules export only a few members (BTW, why not simply support modules?). Yes, we could consider creating a class library in such situations, but then we will have too many class libraries per solution, and they would be too granular. So a module (set of classes and rules about what's exposed and what's not) or friend classes would be more appropriate and useful to develop functional modules within the class libraries. |
Beta Was this translation helpful? Give feedback.
-
You can split the class definition over multiple files using // MyClass.cs
public partial class MyClass {
private int value;
public string M() {
var helper = new NestedHelper(this);
return helper.DoWork();
}
} // MyClass.NestedHelper.cs
public partial class MyClass {
private class NestedHelper {
private readonly MyClass instance;
public NestedHelper(MyClass instance) =>
this.instance = instance;
public string DoWork() =>
$"The value is {instance.value}.";
}
}
Friend classes aren't a requirement for modularity. Plenty of CLR-based libraries have no issues with modularity. Friend classes are something that doesn't exist in the CLR. The relationship between nested and parent classes are as close as you get. The only ways that C# could emulate this behavior is either by making the members in question actually |
Beta Was this translation helpful? Give feedback.
-
It's normal that compiler does things that developers don't have to do manually. I would say that's a good reason to implement this feature, not the blocker. e.g., we could probably define delegate classes ourselves but instead, we can get away with lambdas. I understand the CLR limitation and now I will consider nesting of classes if that's what the compiler would do. I don't know how plenty of CLR-based libraries don't have issues with modularity. I'd say that's probably because modularity is not so well understood. I don't think that node.js or other modular javascript frameworks/runtimes would be the same without modules. Thank you! |
Beta Was this translation helpful? Give feedback.
-
@HaloFour out of curiosity, is there any hint that would allow me to achieve the friend class association in one-to-many fashion? Specifically, I want a single helper class only to be used from a couple of other classes and nowhere else. Partial class solves this problem for one-to-one relation only. |
Beta Was this translation helpful? Give feedback.
-
No. The special relationship in the CLR only exists between nested classes and their parent class. |
Beta Was this translation helpful? Give feedback.
-
If the "other" classes share a common ancestor, you could nest the helper class in that ancestor using the protected modifier: using System;
public abstract class Foo
{
protected class Helper
{
}
}
public class Bar : Foo
{
private readonly Helper _helper = new Helper();
}
public class Baz : Foo
{
private readonly Helper _helper = new Helper();
}
public class Snuff
{
// private readonly Helper _helper = new Helper(); // Helper is inaccessible
// private readonly Foo.Helper _helper = new Helper(); // Helper is inaccessible
} But, while this works, it's a maintenance nightmare for anyone having to work on the code in the future. |
Beta Was this translation helpful? Give feedback.
-
Friend cannot pry open some random class. Like public, friend can only be used by a class to intentionally grant access. Under no circumstances can friend be used to invade the privates of an unsuspecting class. Friend and public also have the following symmetry:
The need for public is glaring and unmistakable. The need for friend is much less common, but no less rational.
I agree with all of this on encapsulation. If every encapsulated system could be represented by a single class then there would be no rationale for friend. Friend allows one to create a perfectly encapsulated, indivisible system that spans more than one class without leaking any private state to unintended classes. These must be tested together as a unit, or the test is meaningless. The involved classes are collectively providing a single, indivisible interface. 2 classes in a friend network are not modular with respect to each other, any more than 2 private members inside a single class are modular with respect to each other. |
Beta Was this translation helpful? Give feedback.
-
A few observations about the stated workarounds (and a few others I pondered over)
A quick note from the C++ docs as well to support the response to the possible critique of encapsulation:
I also cannot think of a way where it would allow access for a specific method or function. |
Beta Was this translation helpful? Give feedback.
-
I like that symmetry, and that also answers my 2 questions about friending specific members (not supported).
C++'s restriction for friendship like this also answers my other question (and was what I was leaning towards anyway). If you want a friend's children to be friends, you must explicitly declare them so. |
Beta Was this translation helpful? Give feedback.
-
I have not been here for a while, but I found another compelling (for me) reason to have friend classes support in c# - Law of Demeter (https://en.wikipedia.org/wiki/Law_of_Demeter). I'm sure you know what this is about, but let me clarify for everybody. Suppose you have a class that holds instances of other classes within and exposes them via a property. e.g., Company class has Addresses property and each of the values is an Address class. Now, you are a designer of the Company class, and you want to disallow calling a certain method on the Address directly and allow doing so only through the Company's method. This is a typical situation if you follow DDD for instance and want to control the entire aggregate's consistency via its root class (Company). If there were friend classes, I would make the method of Address that I want to hide friend towards Company, and thus it would not be callable from the outside of the Company aggregate. And I would define the method on the company that calls the "hidden"/friend method. Without the friend class, I am forced to make the method on the Address public just to call it from the Company. However, I am also exposing the hidden method with that to the outside world. I don't like that, it does not allow me to design classes based on the Law of Demeter cleanly. I know now somebody will ask for an example. Here it is: class Company
{
public IReadOnlyCollection<Address> Addresses { get; }
//Hint because friend classes are not supported in c#: I hope that you will call this method when you need to associate addresses.
public void AssociateAddresses(int index1, int index2)
{
this.Addresses[index1].AssociateWith(this.Addresses[index2]);
//and, other way around too! otherwise the aggregate consistency is violated.
this.Addresses[index2].AssociateWith(this.Addresses[index1]);
}
}
class Address
{
//Hint because friend classes are not supported in c#: don't call this method directly, call Company.AssociateAddresses instead.
//if you call this without associating both ways, aggregate consistency is violated.
public void AssociateWith(Address otherAddress)
{
this._assocaitedAddresses.Add(otherAddress.Id);
}
} Maybe there is a workaround that does not come to me immediately? |
Beta Was this translation helpful? Give feedback.
-
It's kind of crazy how polarized people are about this issue. I agree intensely with @ttutisani. Maybe it's a difference in backgrounds, but I would strongly disagree that any of the workarounds in this thread completely solve the functionality that |
Beta Was this translation helpful? Give feedback.
-
Let me state what I was trying to say again. Just as it's difficult to sell a thing to a person who does not need it, it is almost (or rather without "almost") impossible to convince people who are responsible for C# language design to have "friend" classes. I would like to hear an opinion of a person responsible for the C# language (the resisting side so far) who has tried to walk in our shoes. No arguing, no debate, just an opinion that will indicate that you heard us. Am I asking for a too hard thing to do? Imagine (imagine) that you need the friend class functionality. Come up with realistic use cases that require the friend class. List them all. For each use case, describe how you would do that without the friend class support. Create a GitHub wiki or Docs page explaining the approaches, as well as pros and cons. If you cannot imagine that you really need the friend class, maybe you cannot create a good document, and we need somebody else. The major difference between those asking for the friend class and those who respond, claiming it's not necessary, is that those who asked for it have been on both sides (used C# as is, found a need for the friend class, tried to solve problems without the friend class, and broke their neck). On the other hand, those who are responding seem to have not tried to "like" the friend class. Try to explain its proper usages, and then describe how C# (not having the friend class) can still nicely and cleanly achieve the same results. I did hear a good point about ROI, and maybe that needs to be emphasized. Maybe indeed, building and supporting the friend class feature would cost more than the satisfaction it delivers to its consumers. I can live with that answer. Product development needs to prioritize requirements based on their impact. So maybe that is what we are receiving here. Not enough impact potential. Alternatively, I am thinking, maybe the friend classes (or modularity) is not a language feature but rather a technology built upon it? Currently, there is no way to build modular code with C#. Yes, assemblies somewhat allow doing so, but can you make them so cheap that having 100s (or 1000s) of assemblies per solution would load fine? Maybe this is the answer? I know that assemblies will not allow the granular control on the member level (member of a class is in the friend's visibility so that makes things harder), but still, something. Do you see it? I am trying to find the answer. I am not arguing. I am not pushing, just trying to figure out how to do what I need to do with reasonable costs. Can you do the same and help us out, please? It does not have to be so hard. There are other ways, and you (those who know C# language product priorities, roadmap and abilities) can point us in the right direction much easier than I would. Otherwise, tell us that you don't have the answer, but ROI is too low to even think about it. Fine, I will keep this in the notes and come back to it when time allows (or other smarter folks will do so sooner). Again, now I will receive many quoted answers explaining how each statement I made is not how it's supposed to be. And again, I will take that as coming from a person who has not walked in my shoes. Need a different perception from me, try a different answer. Thank you! |
Beta Was this translation helpful? Give feedback.
-
Fwiw, I run into cases where I think friends might be useful about once every two to three years. However, when that happens I often after back and look at what I'm creating and think that there's often something fishy about the design. This often leads to a rethinking of what I want this component to provide, and how I should organize it differently to encapsulate the functionality I want to be public and what should not be. In the end, I've always found a nicer approach that has precluded the need for 'friend'. As such, I've never found this an area worth championing, or something that I would really support on the ldm. |
Beta Was this translation helpful? Give feedback.
-
I know it's a bit of a cliché by now, but an analyser combined with I can see why the language team is resistant to adding this to the language. Most language suggestions could be implemented with some combination of source generators and analysers; those that get upgraded to language features must meet the following bar:
If you meet most of these criteria you stand a chance of becoming a language feature, but I don't think friend classes meets this bar. It's a bit too niche, it's easily implemented with an analyser, and I can easily see that with its use library designers can easily tie themselves up into knots. |
Beta Was this translation helpful? Give feedback.
-
I've had may times where i needed that feature and always just found my way back here. For me it's using game engines that suggest or dictate a certain way of working with scripts, assemblies and inheritance. |
Beta Was this translation helpful? Give feedback.
This comment was marked as disruptive content.
This comment was marked as disruptive content.
-
@mdmozibur this discussion has few upvotes and lots of down votes. Length of time is not relevant here. If this isn't a good idea for her language then it won't be done, regardless of how long it has been. Currently this has no champion, meaning no one on the ldm views this as something that feel is worthwhile to move forward on. |
Beta Was this translation helpful? Give feedback.
-
The problem with nested classes is that I normally would like outer class to access properties of the inner class but not the other way around. For example, here is class |
Beta Was this translation helpful? Give feedback.
-
For what it's worth, I run into a need for There are certain semantics that seem to be common to every case where I've encountered it:
(Example use cases, among others: Builder patterns, validator patterns, serializer patterns, manager/owner patterns, unit-test patterns, and a whole raft of others: There are lots and lots and lots more I've encountered.) This is arguably the only feature of C++ I wish existed in C#. For those who've said, "Well, other languages don't have it," that's a pretty bad argument: Many languages didn't have garbage collection in 1995, but that didn't make GC a bad idea. Many languages didn't have lambdas or lexical closures in 2001, but that didn't make lambdas a bad idea. Many languages don't have true continuations today, but that doesn't make them a bad idea either. Languages build on top of each others' work, and even if Access controls are a lot like keys to doors in a building. In some cases, you always leave the doors unlocked and There's no good answer every time I've run into this, and it keeps coming up: The language insists that when I draw Venn diagrams for access control, the circles may only fully contain other circles, and may never have partial overlap. So I'm forced to usually just declare methods to be |
Beta Was this translation helpful? Give feedback.
-
I disagree on the validity of this specific point. Unit tests should test the public surface of an assembly. If your unit test class needs to fiddle with the internals of the code under test then you have designed either the tests or the code badly. |
Beta Was this translation helpful? Give feedback.
-
I encountered this situation... class CommandMgr(){ |
Beta Was this translation helpful? Give feedback.
-
This is something I frequently feel the necessity of in c#. In many cases, I want to make the member of a class accessible to another class, which has no relationship of hierarchy. Most probably this feature is available in c++.
Beta Was this translation helpful? Give feedback.
All reactions