Reputation: 1995
Yeah, this question seems stupid. But how can I do that in C#? As we all know, it is easy for C++. If I have 10000000 members in one class, I need set the privilege one by one?
Upvotes: 3
Views: 3057
Reputation: 2935
I had also an aesthetic problem and wrote a little source generator, so I now only have to put an attribute [GeneratePropertiesForAllPrivateVariables]
and a partial
before the class
instead of repeating the public
keyword endlessly.
It basically looks like this:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace MemberAccess
{
[AttributeUsage(AttributeTargets.Class/* ToDo: | System.AttributeTargets.Struct */, AllowMultiple = false, Inherited = false)]
public sealed class GeneratePropertiesForAllPrivateVariablesAttribute : Attribute
{
}
[Generator]
public class GeneratePropertiesForAllPrivateVariables : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
}
public void Execute(GeneratorExecutionContext context)
{
#if DEBUG
if (!Debugger.IsAttached)
{
// Debugger.Launch();
}
#endif
var classesWithAttribute = context.Compilation.SyntaxTrees
.SelectMany(st => st.GetRoot()
.DescendantNodes()
.Where(n => n is ClassDeclarationSyntax)
.Select(n => n as ClassDeclarationSyntax)
.Where(r => r.AttributeLists
.SelectMany(al => al.Attributes)
.Any(a => a.Name.GetText().ToString() == "GeneratePropertiesForAllPrivateVariables")));
foreach (var declaredClass in classesWithAttribute)
{
if (declaredClass.Members.Count > 0)
{
// ToDo: Check for public partial class modifiers here
string className = declaredClass.Identifier.ToString();
var generatedClass = this.GenerateClass(declaredClass);
foreach (var classMember in declaredClass.Members)
{
// is field declaration?
if (classMember.Kind().ToString() == "FieldDeclaration")
{
var fieldDeclaration = (classMember as FieldDeclarationSyntax);
// and is private variable?
if (fieldDeclaration != null
&& fieldDeclaration.Declaration is VariableDeclarationSyntax
&& classMember.Modifiers.Where(token => token.Text == "public").Count() == 0)
{
var variableDeclaration = fieldDeclaration.Declaration as VariableDeclarationSyntax;
var declarator = variableDeclaration.DescendantNodes().Where(n => n is VariableDeclaratorSyntax).First() as VariableDeclaratorSyntax;
if (declarator != null)
{
string privateIdentifier = declarator.Identifier.ToString();
if (!string.IsNullOrEmpty(privateIdentifier))
{
// strip possible 'private' modifier
foreach (var modifier in classMember.Modifiers)
if (modifier.Text == "private")
classMember.Modifiers.Remove(modifier);
// get uppercase identifier for public accessors
string? publicIdentifier = null;
if (char.IsLower(privateIdentifier[0]))
publicIdentifier = privateIdentifier[0].ToString().ToUpper() + privateIdentifier.Substring(1);
else if (privateIdentifier[0] == '_')
publicIdentifier = privateIdentifier[1].ToString().ToUpper() + privateIdentifier.Substring(2);
else if (privateIdentifier.Substring(0, 2) == "m_")
publicIdentifier = privateIdentifier[2].ToString().ToUpper() + privateIdentifier.Substring(3);
if (publicIdentifier != null)
{
// ToDo: didn't gigure out how to replace the private identifier with public one in the declarator
// so using a hack with Sting.Replace in GeneratePropery :-/
this.GeneratePropery(ref generatedClass, classMember.ToString(), privateIdentifier, publicIdentifier);
}
}
}
}
}
}
this.CloseClass(generatedClass);
context.AddSource($"{GetNamespace(declaredClass)}_{className}.g", SourceText.From(generatedClass.ToString(), Encoding.UTF8));
}
}
}
private StringBuilder GenerateClass(ClassDeclarationSyntax c)
{
var sb = new StringBuilder();
sb.Append(@"
using System;
using System.Collections.Generic;
namespace ");
sb.Append(GetNamespace(c));
sb.Append(@"
{
public partial class " + c.Identifier);
sb.Append(@"
{");
return sb;
}
private void GeneratePropery(ref StringBuilder builder, string declaration, /*FieldDeclarationSyntax fds,*/ string privId, string pubId)
{
string replaceIdentifier = declaration.Replace(privId, pubId); // ToDo: make sure that Replace only hits once -- or even better, find out
string removeSemicolon = replaceIdentifier; // how to replace elements of a syntax and pass that as argument.
if (removeSemicolon[removeSemicolon.Length - 1] == ';')
removeSemicolon = removeSemicolon.Substring(0, removeSemicolon.Length - 1);
string decl = $"public {removeSemicolon}";
string getter = $"get => {privId};";
string setter = $"set => {privId} = value;";
builder.AppendLine(@"
" + decl + @"
{
" + getter + @"
" + setter + @"
}");
}
private void CloseClass(StringBuilder generatedClass)
{
generatedClass.Append(
@" }
}");
}
// determine the namespace the class/enum/struct is declared in, if any
private string GetNamespace(BaseTypeDeclarationSyntax syntax)
{
// If we don't have a namespace at all we'll return an empty string
// This accounts for the "default namespace" case
string nameSpace = string.Empty;
// Get the containing syntax node for the type declaration
// (could be a nested type, for example)
SyntaxNode? potentialNamespaceParent = syntax.Parent;
// Keep moving "out" of nested classes etc until we get to a namespace
// or until we run out of parents
while (potentialNamespaceParent != null &&
potentialNamespaceParent is not NamespaceDeclarationSyntax
&& potentialNamespaceParent is not FileScopedNamespaceDeclarationSyntax)
{
potentialNamespaceParent = potentialNamespaceParent.Parent;
}
// Build up the final namespace by looping until we no longer have a namespace declaration
if (potentialNamespaceParent is BaseNamespaceDeclarationSyntax namespaceParent)
{
// We have a namespace. Use that as the type
nameSpace = namespaceParent.Name.ToString();
// Keep moving "out" of the namespace declarations until we
// run out of nested namespace declarations
while (true)
{
if (namespaceParent.Parent is not NamespaceDeclarationSyntax parent)
{
break;
}
// Add the outer namespace as a prefix to the final namespace
nameSpace = $"{namespaceParent.Name}.{nameSpace}";
namespaceParent = parent;
}
}
// return the final namespace
return nameSpace;
}
}
}
Maybe this is heresy to C# purists, but works for me.
Note: The decorated class currently still needs to be public
and non-static
.
I also added a GitHub repo for your convenience.
Upvotes: 0
Reputation: 105
This is a short timesaver, but a timesaver nonetheless. Instead of going through and typing each public
one by one, hold alt
while you click in front of each member. This puts a cursor in each place you click. Once you have alt+click
ed in front of every member you want to be public, type public
followed by a space, just like you would in front of a single member. This way, you put public
in front of all members in one fell swoop.
Upvotes: 0
Reputation: 370
You cannot set the modifier of a class while runtime. If you're trying to set all the members of your type instance or get all of them.. use reflection. A simple example scenario would be..
You got a class..
public class MyFabulousClass {
private object FabulousPropertyMember { get; set; }
// and so on ...
}
And you want to set all its property members from type Object to a given value..
foreach (var member in typeof(MyFabulousClass).GetFields( BindingFlags.NonPublic ).Where(i => i.FieldType == typeof(Object)))
{
member.SetValue(instance, default(Object));
// do other stuff..
}
That would set all non-public members to the default value for type Object
I don't know what you're trying to achieve but that would give you the ability to manipulate the values. Otherwise you would have to write your own runtime compiling thing which changes code while its runtime, but that would be very complex .
Upvotes: 0
Reputation: 191
Assuming you have som kind of class file, I would try to use RegEx (sorry, I can't provide example as I'm not good with RegEx)
Perhabs some plugins (for VS) have posibilities to do such operations (Resharper or similar)
Upvotes: 0
Reputation: 368
read class definition from the file where every member start with new line. then write a new file by appending every line with "public" you can achieve this. and just remove some unwanted public from file you have your desired class. see how to read and write a file in c# http://www.tutorialspoint.com/csharp/csharp_text_files.htm
Upvotes: 0
Reputation: 64943
If I have 10000000 members in one class
There's no such feature in C#, but anyway you shouldn't design classes with such number of members since you're breaking the single responsibility principle.
Properly design your classes, use a good separation of concerns and keep it simple (KISS) and you won't face the issue of having to turn thousands of members from one visibility to other one.
Upvotes: 0
Reputation: 12102
You can't do this in C#. The language requires each member to be individually set to public.
Incidentally, if you have a class with 10000000 members you all want to make public you have far bigger problems than the typing effort to do it.
Upvotes: 6
Reputation: 1006
Each and every method and variable needs to have its own public declaration at the start. Sorry!
If you have a lot of methods that need changing, I'd recommend using find and replace.
Upvotes: 2