c#将类的所有成员设置为public
本文关键字:成员 设置 public | 更新日期: 2023-09-27 18:05:47
是啊,这个问题似乎很蠢。但如何在c#中做到这一点呢?我们都知道,用c++很容易。如果我在一个类中有10000000个成员,我需要一个一个地设置特权?
你不能在c#中这样做。该语言要求将每个成员单独设置为public。
顺便说一下,如果你有一个有10000000成员的类,你都想把它公开,那么你就会遇到比打字更大的问题。
每个方法和变量都需要在开始时有自己的公共声明。对不起!
如果您有很多方法需要更改,我建议使用查找和替换
如果一个类中有10000000个成员
c#中没有这样的特性,但无论如何你不应该设计有这么多成员的类,因为你违反了单一职责原则。
正确地设计你的类,使用良好的关注点分离并保持简单(KISS),你就不会面临不得不将成千上万的成员从一种可见性转移到另一种可见性的问题。
从每个成员以新行开始的文件中读取类定义。然后通过在每一行加上"public"来写一个新文件,你可以做到这一点。然后从你想要的类文件中删除一些不需要的公共。看看如何在c#中读写文件http://www.tutorialspoint.com/csharp/csharp_text_files.htm
假设您有某种类文件,我会尝试使用RegEx(对不起,我不能提供示例,因为我不擅长RegEx)
也许一些插件(为VS)有可能做这样的操作(Resharper或类似)
不能在运行时设置类的修饰符。如果你试图设置类型实例的所有成员或获取所有成员。使用反射。一个简单的示例场景是…
你有课…
public class MyFabulousClass {
private object FabulousPropertyMember { get; set; }
// and so on ...
}
你想把它的所有属性成员从类型对象设置为给定的值。
foreach (var member in typeof(MyFabulousClass).GetFields( BindingFlags.NonPublic ).Where(i => i.FieldType == typeof(Object)))
{
member.SetValue(instance, default(Object));
// do other stuff..
}
将所有非公共成员设置为类型Object
的默认值我不知道你想要达到什么目的,但这会让你有能力操纵这些值。否则,你将不得不编写自己的运行时编译程序,它会在运行时更改代码,但这将非常复杂。
这是一个很短的节省时间的方法,但仍然是一个节省时间的方法。而不是逐一浏览和输入每个public
,按住alt
,同时单击每个成员的前面。这将在您单击的每个位置放置一个光标。一旦你在每个你想公开的成员前面加上alt+click
,输入public
后加一个空格,就像你在单个成员前面一样。这样,您就可以一次性将public
放在所有成员的前面。
我也有一个美学问题,写了一个小的源生成器,所以我现在只需要在class
之前放一个属性[GeneratePropertiesForAllPrivateVariables]
和一个partial
,而不是无休止地重复public
关键字。
它基本上是这样的:
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;
}
}
}
也许这对c#纯粹主义者来说是异端邪说,但对我来说很有用。
注意:装饰类目前仍然需要public
和非static
。
为了你的方便,我还添加了一个GitHub repo。