英文:
How to implement a generic type member of a class?
问题
我有一个名为CarInfo
的类,用于保存汽车和其所有者的信息。所有者可以是已知的客户(即我们可以查找并填充一个Customer
类),或者我们只知道客户的姓名(在这种情况下,我们无法填充Customer类)。我有一个Blazor表单,公开了CarInfo类。关于客户信息成员,它可以是Customer
,也可以只是名字(名字和姓氏)。在这种情况下,如何设计这个类?
public CarInfo
{
public Guid Id { get; }
...
public Customer? CustomerInfo { get; set; } // 要么这个成员被填充
public string? FirstName { get; set; } // 要么只有名字和姓氏成员被填充
public string? LastName { get; set; }
...
}
最好的设计是什么?我是否应该将Customer和名字/姓氏分成一个新的类,例如CustomerInfo
,然后在那里处理多态性?
public class CustomerInfo
{
public Customer? Customer { get; private set; }
public string? FirstName { get; private set; }
public string? LastName { get; private set; }
public CustomerInfo(Customer customer) => Customer = customer;
public CustomerInfo(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
然后像这样使用它?
public CarInfo
{
public Guid Id { get; }
...
public CustomerInfo CustomerInfo { get; set; }
...
}
我在如何处理这种情况的最佳实践或设计模式方面有点困惑。
英文:
I have a class CarInfo
that holds information of a car and its owner. The owner can be either a known customer (i.e. we can look it up and populate a Customer
class) or we only know the name of the customer (in which case we cannot populate a Customer class). I have a Blazor form that exposes the CarInfo class. How do I design this class in respect to the customer info member, which can be either Customer
or just First- and last name?
public CarInfo
{
public Guid Id { get; }
...
public Customer? { get; set; } // Either this member can be populated
public string? FirstName { get; set; } // or the First- and Last name members.
public string? LastName { get; set; }
...
}
What is the best design to handle this? Should I break out Customer and First- and Last name into a new class, e.g. CustomerInfo
and handle this polymorphism there?
public CustomerInfo
{
public Customer? { get; private set; }
public string? FirstName { get; private set; }
public string? LastName { get; private set; }
public CustomerInfo(Customer customer) => Customer = customer;
public CustomerInfo(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
and use it like this?
public CarInfo
{
public Guid Id { get; }
...
public CustomerInfo { get; set; }
...
}
I'm a little stuck in what best practises or patterns should be referred to here.
答案1
得分: 2
在将来的某个时候,我们可能会在C#中添加"判别联合"。
这允许您声明一个类型,该类型只能包含指定一组类型中的一个类型的项目。在您的情况下,唯一允许的类型是Customer
和我将称之为CustomerName
类型。
在那之前,您可以使用OneOf
NuGet包来实现此目的。
您的CarInfo
和支持类型可能如下所示:
public class CarInfo
{
public Guid Id { get; } = Guid.NewGuid();
public OneOf<Customer, CustomerName> CustomerDetails { get; set; }
}
public record CustomerName(string FirstName, string LastName)
{
}
public record Customer(string FirstName, string LastName, string Etc /*Other stuff*/)
{
}
然后,您可以使用CarInfo.CustomerDetails
来初始化CustomerName
或Customer
,但不能使用其他类型:
public static class Program
{
public static void Main()
{
var carInfo1 = new CarInfo
{
CustomerDetails = new Customer("Fred", "Bloggs", "Address")
};
var carInfo2 = new CarInfo
{
CustomerDetails = new CustomerName("Fred", "Bloggs")
};
}
}
当访问CustomerDetails
属性时,通常必须考虑属性可能具有的所有类型,这可以避免忘记处理它们:
carInfo1.CustomerDetails.Switch(
customer => Console.WriteLine($"Full customer available: {customer}"),
customerName => Console.WriteLine($"Only name and address available: {customerName}")
);
carInfo2.CustomerDetails.Switch(
customer => Console.WriteLine($"Full customer available: {customer}"),
customerName => Console.WriteLine($"Only name and address available: {customerName}")
);
请注意,使用OneOf<>
可能不是您情况下的最佳模型,但它确实符合您的描述。然而,您应该考虑其他可能性,比如使用可空属性来处理缺少的信息。
英文:
At some point in the future, we may be getting "Discriminated Unions" added to C#.
These allow you to declare a type which may only contain an item with one of a specified set of types. In your case, the only types allowed are Customer
and what I'm going to call CustomerName
types.
Until that time, you can use the OneOf
NuGet package to do this.
Your CarInfo
and supporting types might look something like this:
public class CarInfo
{
public Guid Id { get; } = Guid.NewGuid();
public OneOf<Customer, CustomerName> CustomerDetails { get; set; }
}
public record CustomerName(string FirstName, string LastName)
{
}
public record Customer(string FirstName, string LastName, string Etc /*Other stuff*/)
{
}
Then you can initialise CarInfo.CustomerDetails
with either a CustomerName
or a Customer
but nothing else:
public static class Program
{
public static void Main()
{
var carInfo1 = new CarInfo
{
CustomerDetails = new Customer("Fred", "Bloggs", "Address")
};
var carInfo2 = new CarInfo
{
CustomerDetails = new CustomerName("Fred", "Bloggs")
};
}
}
When you access the CustomerDetails
property you would generally have to account for all the types that the property might have - this avoids forgetting to deal with them all:
carInfo1.CustomerDetails.Switch(
customer => Console.WriteLine($"Full customer available: {customer}"),
customerName => Console.WriteLine($"Only name and address available: {customerName}")
);
carInfo2.CustomerDetails.Switch(
customer => Console.WriteLine($"Full customer available: {customer}"),
customerName => Console.WriteLine($"Only name and address available: {customerName}")
);
Note that using OneOf<>
may not be the best model for your situation, but it does fit with your description. You should, however, consider other possibilities such as using nullable properties for the missing information.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论