|
I have product that can be connected to a PC via a USB-cable. When I connect it, I can see that the following shows up in the Device Manager under "Ports (COM & LPT)":
USB Serial Device (COM1)
Inside my C# application, I am able to query the descriptive string, "USB Serial Device". However, if I run my C# application on a computer with a French operating system, then my C# application reads "Périphérique série USB" instead. How can I obtain a list of what "USB Serial Device" is called in different languages around the world, without having to install operating systems in all possible languages on my computer?
|
|
|
|
|
|
|
Luc just pointed out what you're really looking for. Sorry, I didn't go back and look up your history.
|
|
|
|
|
Hi Dave,
I've been following arnold_w's attempts to pick the right serial port when many are available, as I am having a similar problem sometimes. IMO the SerialPort class isn't really helping out as it doesn't treat hardware characteristics of the serial ports it knows are present: Are they motherboard internal (e.g. old modem), motherboard external, USB, or whatever.
A simple question would be: when I want to use the USB-to-serial cable that is plugged into a specific USB port somewhere, what then is the one and only serial port name I should use to get at it?
The situation gets harder when multiple USB-to-serials are being used.
And then in one of my applications I have one or two serials plus several USB-based webcams, with the same question: which is which? (those cheap bastards don't have a built-in serial number, they all look absolutely identical!)
Any help is welcome.
|
|
|
|
|
Ah. I didn't look up the history and some description like this wasn't in the OP's question.
My bad.
|
|
|
|
|
Hi,
1.
SerialPort does enumerate all serial ports as it should, there is no problem, except it does not know about the underlying hardware, so for instance you can't differentiate motherboard vs USB ports.
2.
I trust USB is called USB in all languages that use latin character sets, so you could use String.Contains on the friendlyname.
3.
I have my own class enumerating all serial ports, its underlying technology is somewhat similar to the class you referred; here it is, use at your leisure. It returns a list of struct ComInfo which holds the friendlyname as well as the location (for USB serial ports this is a concatenation of port numbers, pointing to the USB port actually used).
BTW: you will have to remove all lines that hold an "env", these are used for my debugging only.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace LP_Core {
public class LP_SerialPortInfo {
private static List<ComInfo> serialPorts=new List<ComInfo>();
private const UInt32 DIGCF_PRESENT=0x00000002;
private const UInt32 DIGCF_DEVICEINTERFACE=0x00000010;
private const UInt32 DICS_FLAG_GLOBAL=0x00000001;
private const UInt32 DIREG_DEV=0x00000001;
private const UInt32 KEY_QUERY_VALUE=0x0001;
private const string GUID_DEVINTERFACE_COMPORT="86E0D1E0-8089-11D0-9CE4-08003E301F73";
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA {
public Int32 cbSize;
public Guid ClassGuid;
public Int32 DevInst;
public UIntPtr Reserved;
};
public enum ComPortCharacteristic {
Description=0x00000000,
FriendlyName=0x0000000C,
HardwareID=0x00000001,
Location=0x0000000D
}
[DllImport("setupapi.dll")]
private static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
[DllImport("setupapi.dll")]
private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, Int32 MemberIndex, ref SP_DEVINFO_DATA DeviceInterfaceData);
[DllImport("setupapi.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData,
uint property, out UInt32 propertyRegDataType, StringBuilder propertyBuffer, uint propertyBufferSize, out UInt32 requiredSize);
[DllImport("setupapi.dll", SetLastError=true)]
private static extern IntPtr SetupDiGetClassDevs(ref Guid gClass, UInt32 iEnumerator, IntPtr hParent, UInt32 nFlags);
[DllImport("Setupapi", CharSet=CharSet.Auto, SetLastError=true)]
private static extern IntPtr SetupDiOpenDevRegKey(IntPtr hDeviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, uint scope,
uint hwProfile, uint parameterRegistryValueKind, uint samDesired);
[DllImport("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueExW", SetLastError=true)]
private static extern int RegQueryValueEx(IntPtr hKey, string lpValueName, int lpReserved, out uint lpType,
StringBuilder lpData, ref uint lpcbData);
[DllImport("advapi32.dll", CharSet=CharSet.Ansi, SetLastError=true, ExactSpelling=true)]
private static extern int RegCloseKey(IntPtr hKey);
[DllImport("kernel32.dll")]
private static extern Int32 GetLastError();
public struct ComInfo {
public string Name;
public string Description;
public string FriendlyName;
public string Location;
public string HardwareID;
}
public static List<ComInfo> GetComPorts() {
serialPorts.Clear();
Guid guidComPorts=new Guid(GUID_DEVINTERFACE_COMPORT);
IntPtr hDeviceInfoSet=SetupDiGetClassDevs(
ref guidComPorts, 0, IntPtr.Zero, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if (hDeviceInfoSet!=IntPtr.Zero) {
try {
List<ComInfo> devices=new List<ComInfo>();
Int32 iMemberIndex=0;
while (true) {
SP_DEVINFO_DATA deviceInfoData=new SP_DEVINFO_DATA();
deviceInfoData.cbSize=Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
bool success=SetupDiEnumDeviceInfo(hDeviceInfoSet, iMemberIndex, ref deviceInfoData);
if (!success) {
break;
}
ComInfo comInfo=new ComInfo();
comInfo.Name=getName(hDeviceInfoSet, deviceInfoData);
comInfo.Description=getString((UInt32)ComPortCharacteristic.Description, hDeviceInfoSet, deviceInfoData);
comInfo.FriendlyName=getString((UInt32)ComPortCharacteristic.FriendlyName, hDeviceInfoSet, deviceInfoData);
comInfo.HardwareID=getString((UInt32)ComPortCharacteristic.HardwareID, hDeviceInfoSet, deviceInfoData);
comInfo.Location=getString((UInt32)ComPortCharacteristic.Location, hDeviceInfoSet, deviceInfoData);
env.log("found: "+comInfo.Name+" = "+comInfo.Description+";FN="+comInfo.FriendlyName+
"; HID="+comInfo.HardwareID+"; LOC="+comInfo.Location);
serialPorts.Add(comInfo);
iMemberIndex++;
}
} finally {
SetupDiDestroyDeviceInfoList(hDeviceInfoSet);
}
}
return serialPorts;
}
private static string getName(IntPtr pDevInfoSet, SP_DEVINFO_DATA deviceInfoData) {
string name = "???";
IntPtr hDeviceRegistryKey = SetupDiOpenDevRegKey(pDevInfoSet, ref deviceInfoData,
DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
if (hDeviceRegistryKey!=IntPtr.Zero) {
StringBuilder buf = new StringBuilder(256);
try {
uint lpRegKeyType;
uint length = (uint)buf.Capacity;
int result = RegQueryValueEx(hDeviceRegistryKey, "PortName", 0, out lpRegKeyType, buf, ref length);
if (result!=0) {
throw new Exception("Can not read registry value PortName for device "+deviceInfoData.ClassGuid);
}
} finally {
RegCloseKey(hDeviceRegistryKey);
}
name=buf.ToString();
}
return name;
}
private static string getString(UInt32 SPDRP, IntPtr hDeviceInfoSet, SP_DEVINFO_DATA deviceInfoData) {
StringBuilder buf = new StringBuilder(256);
uint propRegDataType;
uint length = (uint)buf.Capacity;
bool success = SetupDiGetDeviceRegistryProperty(hDeviceInfoSet, ref deviceInfoData, SPDRP,
out propRegDataType, buf, length, out length);
if (!success) {
throw new Exception("Can not read registry value PortName for device "+deviceInfoData.ClassGuid);
}
return buf.ToString();
}
}
}
|
|
|
|
|
Luc Pattyn wrote: 1.
SerialPort does enumerate all serial ports as it should, there is no problem, except it does not know about the underlying hardware, so for instance you can't differentiate motherboard vs USB ports. Then it was a performance issue, I remember I couldn't use this class for a reason. I also refresh the list on the fly each time a USB-cable is connected/disconnected so if I remember correctly it was annoying.
Luc Pattyn wrote: 2.
I trust USB is called USB in all languages that use latin character sets, so you could use String.Contains on the friendlyname. In my case it's not enough to find anything that contains "USB", I also need to categorize the cable/device and distinguish "USB Serial Device" from "USB Serial Port", my C# application supports products that show up as either one of these.
Luc Pattyn wrote: I have my own class enumerating all serial ports, its underlying technology is somewhat similar to the class you referred; here it is, use at your leisure. It returns a list of struct ComInfo which holds the friendlyname as well as the location (for USB serial ports this is a concatenation of port numbers, pointing to the USB port actually used). It has a lot of similarities with the code I'm already using (I slightly modified this code: Svetlin Nakov – Official Web Site and Blog » Enumerate All COM Ports and Find Their Name and Description in C#[^] ). Are you saying your code will always return a description string in English, even if the operating system is non-English or what would I gain be switching to your code?
|
|
|
|
|
Could you pick the information you need from WMI?
When I open \\.\ROOT\CIMV2 class Win32_SerialPort, I see one COM1 instance, which has 47 properties (some of which are empty values), like Availability, Caption, Name, ... ProviderType, ...
Maybe you have what you need (and then some!) there. As I do not have a USB COM-port at the moment, I can't tell what it wouold look like in WMI, but try it out!
If you haven't got a WMI browser: One that is reasonable OK to use is CodePlex Archive WMI Explorer[^]. Once you have found what you are looking for, setting up a search in C# code is fairly simple. Like this I use to get information on disk partitions:
ManagementScope scope = new ManagementScope(@"\\.\root\microsoft\windows\storage");
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM MSFT_Partition")) {
scope.Connect();
searcher.Scope = scope;
foreach (ManagementObject queryObj in searcher.Get()) {
char driveletter = (char)queryObj["DriveLetter"];
if (driveletter == '\0') continue;
FsVolume part = new FsVolume();
part.driveletter = driveletter;
part.diskNumber = (uint) queryObj["DiskNumber"];
part.partNo = (uint) queryObj["PartitionNumber"];
part.partGuid = (string)queryObj["UniqueID"];
partList.Add(part);
}
}
Your code will obviously address other classes and properties - you'll find them using WMI Explorer (or some other WMI browser).
This won't give you "USB Serial Device" in all the languages of the world, but I assume that if you find the information you need by inspecting properties from WMI, it doesn't really matter what a rose is called in the current language.
|
|
|
|
|
In my old ( >10 years ago) code I was using this:
public StatusType getAvailableComPorts()
{
StatusType status = new StatusType(StatusCodeType.OK, null, "", "");
try
{
string[] availableComPorts = SerialPort.GetPortNames();
if ((availableComPorts == null) || (availableComPorts.Length <= 0))
{
status.statusCode = StatusCodeType.OTHER_ERROR;
status.errorHeading = "No Com Ports Available";
status.errorMessage = "No available com port was found on your computer. Please add a com port or use a different computer to control your PTT box.";
return status;
}
Array.Sort(availableComPorts, new ComPortComparerClass());
ComPortInformation[] comPortsInformation = new ComPortInformation[availableComPorts.Length];
for (int i = 0; i < comPortsInformation.Length; i++)
{
comPortsInformation[i] = new ComPortInformation(availableComPorts[i]);
}
if ((availableComPorts != null) && (0 < availableComPorts.Length))
{
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PnPEntity");
for (int i = availableComPorts.Length - 1; 0 <= i; i--)
{
foreach (ManagementObject queryObj in searcher.Get())
{
if ((queryObj["Name"] != null) && (queryObj["Name"].ToString().Contains("(" + availableComPorts[i] + ")")))
{
comPortsInformation[i].comPortDescriptor = queryObj["Caption"].ToString().Replace("(" + availableComPorts[i] + ")", "").Trim();
break;
}
}
}
}
catch (ManagementException)
{
}
}
status.value = comPortsInformation;
}
catch (Exception e)
{
status.statusCode = StatusCodeType.OTHER_ERROR;
status.errorHeading = "Error";
status.errorMessage = "Error getting available com ports. Technical info: " + e.Message;
}
return status;
}
For some reason I stepped away from this (maybe it was this part that was too slow?) and started using the other approach instead.
|
|
|
|
|
arnold_w wrote: Then it was a performance issue, I remember I couldn't use this class for a reason. SerialPort class isn't slow. When using WMI the first WMI call often is slow.
Luc Pattyn wrote: I have my own class enumerating all serial ports, its underlying technology is somewhat similar to the class you referred; here it is, use at your leisure. It returns a list of struct ComInfo which holds the friendlyname as well as the location (for USB serial ports this is a concatenation of port numbers, pointing to the USB port actually used). That is what I said.
|
|
|
|
|
When I use your code I get the following:
found: COM24 = USB Serial Device;FN=USB Serial Device (COM24); HID=USB\VID_0483&PID_5741&REV_0200&MI_00; LOC=0000.0014.0000.002.004.004.000.000.000
Who specifies HID ("USB\VID_0483&PID_5741&REV_0200&MI_00") and LOC ("0000.0014.0000.002.004.004.000.000.000")? Is something from the PC/operating system or from the USB-cable/connected product?
|
|
|
|
|
The hardwareID contains a vendorID and a productID, these obviously come from data inside the device.
The location is where the device currently sits, so obviously it does not come from inside the device.
FYI: this information can also be obtained manually following this path on Win10:
My PC/Properties/Device Manager/Ports...
|
|
|
|
|
Ok, that sounds great, then I will stop comparing the description string and compare the HID string instead. Thank you for your help!
|
|
|
|
|
hi
I have project of type Console .net framework 4.6.1. A simple hello world. No dependencies. No references.
When I right click and try publishing this project, it goes into manifest mode of publishing. Where do I change settings to make it a traditional simpler Publish, so I get a .exe file without manifest files. So that, the Publish folder can be copy pasted to any computer and it runs without issues.
Some research suggested to uncheck LINKER, but this is not available in Projects --> Properties --> Configuration
thank you
|
|
|
|
|
All you do is look in your project bin\Release folder. It's right there every time you compile the project, so long as you change the configuration from Debug to Release.
All you for such a simple app is the .exe.
Now, if the machine you're running the project on doesn't already have the .NET Framework 4.6.1, or higher, installed already, you're going to have to install that before your app will run.
|
|
|
|
|
This query is bit long, and it returns the correct data, but it takes about 4 minutes to run. I'd like to speed it up. The first part with the query runs pretty quick. The FOREACH loop part is the bottleneck.
Inside the FOREACH are 4 queries. They are all the same, except they pull from 4 different tables (JobHardwareVendors, JobLumberVendors, EquipmentHardwareVendors, TrussHardwareVendors). Those tables each have the same structure.
Those inner queries look like this:
/*********************************************************
* Get the Hardware Vendors
*********************************************************/
var vendors = (from v in db.JobHardwareVendors
join c in db.Companies on v.VendorId equals c.Id
where v.JobId == result.Id &&
!v.DeletedDT.HasValue
select new CompanyHeaderEntity
{
Id = c.Id,
CompanyName = c.CompanyName,
StatusId = c.StatusId
}).Distinct().ToList();
result.HardwareCompanies.AddRange(vendors.Where(x => x.StatusId == companyStatuseActive.Id));
foreach (var vendor in vendors)
{
result.VendorInitials += GetInitials(vendor.CompanyName) + ",";
}
Here's the entire method
public async Task<List<TimelineReportEntity>> GetTimelineReportData(GenericReportArgsEntity reportArgs)
{
var t = await Task.Factory.StartNew(() =>
{
List<TimelineReportEntity> results = null;
using (var db = GetDataContext())
{
try
{
IQueryable<TimelineReportEntity> query = (from j in db.Jobs
join p in db.Projects on j.ProjectId equals p.Id
join c in db.Companies on p.CompanyId equals c.Id
join e in db.Employees on j.ForemanId equals e.Id into ep
from e in ep.DefaultIfEmpty()
join lc in db.Companies on j.LumberVendorId equals lc.Id into elc
from lc in elc.DefaultIfEmpty()
join hc in db.Companies on j.HardwareVendorId equals hc.Id into ehc
from hc in ehc.DefaultIfEmpty()
join tc in db.Companies on j.HardwareVendorId equals tc.Id into etc
from tc in etc.DefaultIfEmpty()
where !j.DeletedDT.HasValue
select new TimelineReportEntity
{
Id = j.Id,
JobId = j.JobNumber,
ProjectId = p.Id,
ProjectName = p.ProjectName,
CompanyId = c.Id,
CompanyName = c.CompanyName,
ForemanId = e.Id,
ForemanName = $"{e.FirstName} {e.LastName}",
Phase = j.Phase,
Quantity = j.Quantity,
Notes = j.Notes,
});
ExpressionStarter<TimelineReportEntity> predicate = null;
if (reportArgs.IncludeAllProjects &&
reportArgs.IncludeAllCompanies &&
reportArgs.IncludeAllForemen)
{
results = query.ToList();
}
else
{
predicate = PredicateBuilder.New<TimelineReportEntity>();
if (!reportArgs.IncludeAllProjects)
{
foreach (var projectId in reportArgs.ProjectIds)
{
predicate = predicate.Or(p => p.ProjectId == projectId);
}
}
if (!reportArgs.IncludeAllCompanies)
{
foreach (var companyId in reportArgs.CompanyIds)
{
predicate = predicate.Or(p => p.CompanyId == companyId);
}
}
if (!reportArgs.IncludeAllForemen)
{
foreach (var foremanId in reportArgs.ForemenIds)
{
predicate = predicate.Or(p => p.ForemanId == foremanId);
}
}
results = query.Where(predicate).ToList();
}
var companyStatuses = GetLookups(Constants.LookupCategoryGenericFilters);
var companyStatuseActive = companyStatuses.FirstOrDefault(x => x.AppCode == Constants.LookupCategoryGenericFiltersTypeActive);
foreach (var result in results)
{
result.ForemanInitials = result.ForemanName;
/*********************************************************
* Get the Hardware Vendors
*********************************************************/
var vendors = (from v in db.JobHardwareVendors
join c in db.Companies on v.VendorId equals c.Id
where v.JobId == result.Id &&
!v.DeletedDT.HasValue
select new CompanyHeaderEntity
{
Id = c.Id,
CompanyName = c.CompanyName,
StatusId = c.StatusId
}).Distinct().ToList();
result.HardwareCompanies.AddRange(vendors.Where(x => x.StatusId == companyStatuseActive.Id));
foreach (var vendor in vendors)
{
result.VendorInitials += GetInitials(vendor.CompanyName) + ",";
}
/*********************************************************
* Get the Lumber Vendors
*********************************************************/
vendors = (from v in db.JobLumberVendors
join c in db.Companies on v.VendorId equals c.Id
where v.JobId == result.Id &&
!v.DeletedDT.HasValue
select new CompanyHeaderEntity
{
Id = c.Id,
CompanyName = c.CompanyName,
StatusId = c.StatusId
}).Distinct().ToList();
result.LumberCompanies.AddRange(vendors.Where(x => x.StatusId == companyStatuseActive.Id));
foreach (var vendor in vendors)
{
result.VendorInitials += GetInitials(vendor.CompanyName) + ",";
}
/*********************************************************
* Get the Equipment Vendors
*********************************************************/
vendors = (from v in db.JobEquipmentVendors
join c in db.Companies on v.VendorId equals c.Id
where v.JobId == result.Id &&
!v.DeletedDT.HasValue
select new CompanyHeaderEntity
{
Id = c.Id,
CompanyName = c.CompanyName,
StatusId = c.StatusId
}).Distinct().ToList();
result.EquipmentCompanies.AddRange(vendors.Where(x => x.StatusId == companyStatuseActive.Id));
foreach (var vendor in vendors)
{
result.VendorInitials += GetInitials(vendor.CompanyName) + ",";
}
/*********************************************************
* Get the Truss Vendors
*********************************************************/
vendors = (from v in db.JobTrussVendors
join c in db.Companies on v.VendorId equals c.Id
where v.JobId == result.Id &&
!v.DeletedDT.HasValue
select new CompanyHeaderEntity
{
Id = c.Id,
CompanyName = c.CompanyName,
StatusId = c.StatusId
}).Distinct().ToList();
result.TrussCompanies.AddRange(vendors.Where(x => x.StatusId == companyStatuseActive.Id));
foreach (var vendor in vendors)
{
result.VendorInitials += GetInitials(vendor.CompanyName) + ",";
}
if (!string.IsNullOrEmpty(result.VendorInitials))
{
result.VendorInitials = result.VendorInitials.TrimEnd(',');
}
/*********************************************************
* Get the latest Start Date
*********************************************************/
var startDateRevision = GetJobStartDateRevisions(result.Id).OrderByDescending(x => x.Revision).FirstOrDefault();
if (startDateRevision != null)
{
result.StartDate = startDateRevision.StartDate;
result.WeekGroup = result.StartDate.StartOfWeek(DayOfWeek.Monday);
result.FromDate = reportArgs.FromDate;
result.ToDate = reportArgs.ToDate;
}
}
results = results.Where(x => x.StartDate >= reportArgs.FromDate && x.StartDate <= reportArgs.ToDate).OrderBy(x => x.StartDate).ToList();
}
catch (Exception e)
{
throw;
}
}
return results.ToList();
});
return t;
}
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
<>
-- modified 27-Jun-20 12:06pm.
|
|
|
|
|
No.
That is a poor attempt at a password cracker, which is a product designed to circumvent legitimate security measures.
We do not condone, support, or assist in the production of malicious code in any way, form, or manner. This is a professional site for professional developers.
If you want to know how to create such things, you need to visit a hacking site: but be sure to disable all firewalls and antivirus products first or they won't trust you enough to tell you.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Hi this is just for education purpose. I don't think there is any legitimate system that can be cracked that way.
Obviously most if not all systems will lock an account after a few attempts
|
|
|
|
|
Your homework is to write malicious code?
Yeah, right ... We don't do your homework either, malicious or not.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Member 12588963 wrote: I don't think there is any legitimate system that can be cracked that way.
Oh really? How naive of you to try to write something that's already been done innumerable times then.
Why would anyone want to write something like this? BECAUSE THEY ACTUALLY WORKED ON REAL SYSTEMS!
You don't need to check the hashed password against the real system. All you need is a copy of the account database. You can then run as many checks as you want against the database with nothing "locking you out after a few attempts".
|
|
|
|
|
Hi!
I am trying to find the best way to transform a list of objects and sort based on custom order:
I have a list of profile duplicates, and need to find "top" profile based on a priority order. The top profile need to be transformed into a new object with 3 properties. These 3 properties also dictate the sort order.
The 3 new enum-based properties are:
ProfileType with types (SuperUser > NormalUser > Beginner),
ProfileSubType with types (Internal > External),
ProfileValidity with types (Valid > ValidInFuture > Expire)
The properties are based on enums from an external source and are out of order, so I need to have my own custom sort for each of them.
So e.g. a prioritized list would look like:
{Superuser, Internal, Valid},
{Superuser, Internal, Expired},
{Superuser, External, Valid},
{Normaluser, Internal, Valid},
{Beginner, Internal, Expire}
Right now my solution is this:
TransformedProfile = profileList.Select(x => new TransformedProfile
{
ProfileType = GetProfileType(x.PropertyA),
ProfileSubType = GetProfileSubType(x.PropertyB),
ProfileValidity = GetValidity(x.StartDate, x.ExpiryDate)
})
.OrderBy(y => typePriorityDict[y.ProfileType])
.ThenBy(y => subTypePriorityDict[y.ProfileSubType])
.ThenBy(y => validityPriorityDict[y.ProfileValidity])
.FirstOrDefault();
The typePriorityDict , subTypePriorityDict , validityPriorityDict are dictionaries with my custom sort order.
This solution works ok and gives me the desired result. But I was wondering if there are more efficient ways of doing this?
|
|
|
|
|
You could implement IComparable and IComparer[^] in your class, and as a bonus you gain equality, greater than, and less than operators for the class directly.
Your sorting then becomes a whole load more readable.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
OriginalGriff wrote: as a bonus you gain equality, greater than, and less than operators for the class directly
Only if you implement them yourself, although the implementation is fairly trivial.
public static bool operator <(Foo left, Foo right) => left is null || left.CompareTo(right) < 0;
public static bool operator <=(Foo left, Foo right) => left is null || left.CompareTo(right) <= 0;
public static bool operator >=(Foo left, Foo right) => left != null && left.CompareTo(right) >= 0;
public static bool operator >(Foo left, Foo right) => left != null && left.CompareTo(right) > 0;
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|