patterncsharpMinor
Building a summary string from a flags enum
Viewed 0 times
enumbuildingsummaryflagsfromstring
Problem
I'm trying to replicate the attributes text that appears in File Explorer, for files and directories.
I can't just use the enum names, as the summary text uses a single character for each set flag, and that character isn't always the first character of the enum member. I'm also not using the reserved
Am I going about this in the most efficient way?
I can't just use the enum names, as the summary text uses a single character for each set flag, and that character isn't always the first character of the enum member. I'm also not using the reserved
FileAttribute.Device attribute. So, I'm using a string.join operation to concatenate the flag characters.public string AttributeSummary
{
get
{
FileAttributes fatt = (FileAttributes)_attribs;
return string.Join(string.Empty,
(fatt.HasFlag(FileAttributes.ReadOnly) ? "R" : string.Empty),
(fatt.HasFlag(FileAttributes.Hidden) ? "H" : string.Empty),
(fatt.HasFlag(FileAttributes.System) ? "S" : string.Empty),
(fatt.HasFlag(FileAttributes.Directory) ? "D" : string.Empty),
(fatt.HasFlag(FileAttributes.Archive) ? "A" : string.Empty),
(fatt.HasFlag(FileAttributes.Normal) ? "N" : string.Empty),
(fatt.HasFlag(FileAttributes.Temporary) ? "T" : string.Empty),
(fatt.HasFlag(FileAttributes.SparseFile) ? "P" : string.Empty),
(fatt.HasFlag(FileAttributes.ReparsePoint) ? "L" : string.Empty),
(fatt.HasFlag(FileAttributes.Compressed) ? "C" : string.Empty),
(fatt.HasFlag(FileAttributes.Offline) ? "O" : string.Empty),
(fatt.HasFlag(FileAttributes.NotContentIndexed) ? "I" : string.Empty),
(fatt.HasFlag(FileAttributes.Encrypted) ? "E" : string.Empty),
(fatt.HasFlag(FileAttributes.IntegrityStream) ? "V" : string.Empty),
(fatt.HasFlag(FileAttributes.NoScrubData) ? "X" : string.Empty));
}
}Am I going about this in the most efficient way?
Solution
You should start by separating concerns. Your current
Solution 1 - yield return
So how do we fix it? We create an extension method that will only give us the abbreviations for active flags:
and we then are able to turn the property into one-liner:
By separating them you can tune the
This is still not the most optimal solution becasue the abbreviations are dependant of the active flag so you cannot just get them all at once. There is one more way...
Solution 2 - dictionary
I find however a dictionary solution is nice one.
What you need to do is to create a mapping dictionary for the flags:
Modify the
And write one more extension to explode the flags:
The
I prefer the dictionary because the is the most reusable solution. If you need those abbreviation in other places you don't have to repeat yourself and can use the dictionary in several places.
AttributeSummary does two things:- it maps flags to abbreviations
- it creates the
string
Solution 1 - yield return
So how do we fix it? We create an extension method that will only give us the abbreviations for active flags:
public static IEnumerable ToAbbreviations(this FileAttributes fileAttributes)
{
if (fileAttributes.HasFlag(FileAttributes.ReadOnly)) { yield return "R"; }
if (fileAttributes.HasFlag(FileAttributes.Hidden)) { yield return "H"; }
if (fileAttributes.HasFlag(FileAttributes.System)) { yield return "S"; }
// ...
}and we then are able to turn the property into one-liner:
public string AttributeSummary => string.Join(string.Empty, ((FileAttributes)_attribs).ToAbbreviations());By separating them you can tune the
ToAbbreviations however you like and the AttributeSummary stays unaffected - this is exactly what we want. If you decide to use the optimized HasFlag from @svick you just need to change the extension... which by the way is now much easier to test because you can verify that it returns the correct value for each flag before creating the string.This is still not the most optimal solution becasue the abbreviations are dependant of the active flag so you cannot just get them all at once. There is one more way...
Solution 2 - dictionary
I find however a dictionary solution is nice one.
What you need to do is to create a mapping dictionary for the flags:
private static readonly IReadOnlyDictionary FileAttributesAbbreviations = new Dictionary
{
[FileAttributes.ReadOnly] = "R",
[FileAttributes.Hidden] = "H",
[FileAttributes.System] = "S",
};Modify the
ToAbbreviations extension to use the dictionary:public static IEnumerable ToAbbreviations(this FileAttributes fileAttributes)
{
return fileAttributes.Explode().Select(x => FileAttributesAbbreviations[x]);
}And write one more extension to explode the flags:
public static IEnumerable Explode(this FileAttributes fileAttributes)
{
return Enum.GetValues(typeof(FileAttributes))
.Cast()
.Select(x => fileAttributes & x)
.Where(x => x != 0);
}The
AttributeSummary property does not change but the flags and abbreviation mapping is even easier to understand and to maintain now.I prefer the dictionary because the is the most reusable solution. If you need those abbreviation in other places you don't have to repeat yourself and can use the dictionary in several places.
Code Snippets
public static IEnumerable<string> ToAbbreviations(this FileAttributes fileAttributes)
{
if (fileAttributes.HasFlag(FileAttributes.ReadOnly)) { yield return "R"; }
if (fileAttributes.HasFlag(FileAttributes.Hidden)) { yield return "H"; }
if (fileAttributes.HasFlag(FileAttributes.System)) { yield return "S"; }
// ...
}public string AttributeSummary => string.Join(string.Empty, ((FileAttributes)_attribs).ToAbbreviations());private static readonly IReadOnlyDictionary<FileAttributes, string> FileAttributesAbbreviations = new Dictionary<FileAttributes, string>
{
[FileAttributes.ReadOnly] = "R",
[FileAttributes.Hidden] = "H",
[FileAttributes.System] = "S",
};public static IEnumerable<string> ToAbbreviations(this FileAttributes fileAttributes)
{
return fileAttributes.Explode().Select(x => FileAttributesAbbreviations[x]);
}public static IEnumerable<FileAttributes> Explode(this FileAttributes fileAttributes)
{
return Enum.GetValues(typeof(FileAttributes))
.Cast<FileAttributes>()
.Select(x => fileAttributes & x)
.Where(x => x != 0);
}Context
StackExchange Code Review Q#146265, answer score: 4
Revisions (0)
No revisions yet.