Deconstruct with same number of parameters but different signature -> compiler eror - c#-7.0

I believe that this question is different from the linked duplicate because the linked question and answer don't directly address the same issue (I have added an update below that hopefully illustrates this).
Why is an error returned when there are two Deconstruct methods on a class with the same number of arguments but different types - see code below. Multiple Deconstruct methods with different numbers of arguments are fine. Compiler only complains when the 'Deconstruct' methods have the same number of arguments even though the arguments are different types
I would have thought Deconstruct would work the same way as other methods where methods can be overloaded by signature (same number of arguments, different types). Not sure it actually makes sense for a class to have two Deconstruct methods with different signatures or different number of arguments (see comments), but I was wondering if this error is due to a technical limitation or a design feature to prevent people doing this.
var person = new Person { ID = 123, FirstName = "Bob", MiddleName = "Tim", LastName = "Smith" };
//error CS0121: The call is ambiguous between the following methods or properties: 'Person.Deconstruct(out string, out string, out string)' and 'Person.Deconstruct(out int, out string, out string)'
// Deconstruct instance or extension method was found for type 'Person', with 3 out parameters and a void return type.
(string a, string b, string c) = person; //<=Errors on person here
...
public class Person
{
public bool ID { get; set; }
public int FirstName { get; set; }
public int MiddleName { get; set; }
public int LastName { get; set; }
public void Deconstruct(out int fname, out int mname, out int lname)
{
fname = FirstName;
mname = MiddleName;
lname = LastName;
}
public void Deconstruct(out bool id, out int fname, out int lname)
{
id = ID;
fname = FirstName;
lname = LastName;
}
}
Update
The answer can be found here
Specifically:
if there are overloads how this works then? var (x, y) = foo;
That is, there is no way to tell which overload to call when using var. That got me thinking about what would happen when overloading out methods and assigning to var. E.g. adding the following methods to Person
public void Foo(out int i)
{
i = 1;
}
public void Foo(out bool i)
{
i = false;
}
Then gives the following behaviour:
person.Foo(out int k); //calls Foo(out int i)
person.Foo(out bool m); //calls Foo(out bool i)
//Error CS0121 The call is ambiguous between the following methods or properties: 'Person.Foo(out int)' and 'Person.Foo(out bool)
person.Foo(out var i );
Which seems inconsistent with Deconstruct. Why does method overloading work so long as there is no var, but Deconstruct doesn't allow it even when assigning to different types?

Related

Domain Driven Design Identities and Value Objects

In Vaughn Vernon Implementing Domain-driven Design book and code, he uses a mix of Identity and ValueObject for identifiers.
public interface IIdentity
{
string Id { get; }
}
public abstract class Identity : IEquatable<Identity>, IIdentity
{
protected Identity() => this.Id = Guid.NewGuid().ToString();
protected Identity(string id) => this.Id = id;
// Currently for Entity Framework, set must be protected, not private. will be fixed in EF 6.
public string Id { get; protected set; }
public bool Equals(Identity id)
{
if (object.ReferenceEquals(this, id)) return true;
if (object.ReferenceEquals(null, id)) return false;
return this.Id.Equals(id.Id);
}
public override bool Equals(object anotherObject) => Equals(anotherObject as Identity);
public override int GetHashCode() => (this.GetType().GetHashCode() * 907) + this.Id.GetHashCode();
public override string ToString() => this.GetType().Name + " [Id=" + Id + "]";
}
And value objects:
public abstract class ValueObject
{
// Returns all components of a value objects which constitute its identity.
protected abstract IEnumerable<object> GetEqualityComponents();
public override bool Equals(object obj)
{
if (object.ReferenceEquals(this, obj)) return true;
if (object.ReferenceEquals(null, obj)) return false;
if (this.GetType() != obj.GetType()) return false;
var vo = obj as ValueObject;
return GetEqualityComponents().SequenceEqual(vo.GetEqualityComponents());
}
public override int GetHashCode() => HashCodeHelper.CombineHashCodes(GetEqualityComponents());
}
I see in some cases a ValueObject is used as an identity:
public class TaskId : ValueObject
{
public TaskId()
: this(Guid.NewGuid().ToString().ToUpper().Substring(0, 8))
{
}
public TaskId(string id)
{
AssertionConcern.AssertArgumentNotEmpty(id, "The id must be provided.");
AssertionConcern.AssertArgumentLength(id, 8, "The id must be 8 characters or less.");
this.Id = id;
}
public string Id { get; private set; }
protected override IEnumerable<object> GetEqualityComponents()
{
yield return this.Id;
}
}
And other cases they are a pure Identity:
public class ForumId : SaaSOvation.Common.Domain.Model.Identity
{
public ForumId(string id) : base(id) { }
}
To me, all values should be a value object, including identities, so I don't understand the differentiation here in having an explicit Identity type.
With his demo code, how/why is he choosing the above approaches to modeling identity and is there any advantage/disadvantage over the other?
Both implementations are comparing properties for equality, so both follow the concept of a value object. The Identity class is dedicated to be a reusable value object for custom identity types which still share the same requirements (using an arbitrary string, usually created from a GUID/UUID). Still a value object,but if no other properties than a string id are needed this one can be used in such cases while still strongly typing different types of identities with separate derived classes (like ForumId).
The ValueObject class on the other hand is dealing with arbitrary properties that need to be compared in the respective derived classes.
I think the intention to derive TaskId from ValueObject rather than from Identity becomes clearer if you look at the constructor. In this case a specific business rule - the id must be eight characters or less - needs to be fulfilled upon construction which would not make sense for the definition of the general identity implementation.

Returning a Value Tuple of Custom Objects From Ormlite

I'm trying to do a simple Join on 2 tables and return a Value Tuple of those two tables.
public partial class DeliveryMethod
{
[Required]
[PrimaryKey]
public int DeliveryMethodId { get; set; }
[References(typeof(Event))]
[Required]
public string EventId { get; set; }
[References(typeof(DeliveryType))]
[Required]
public short DeliveryTypeId { get; set; }
[Required]
public int MappedValue { get; set; }
}
public partial class DeliveryType
{
[Required]
[PrimaryKey]
public short DeliveryTypeId { get; set; }
[Required]
public string DeliveryTypeDescription { get; set; }
}
public List<(DeliveryMethod deliveryMethod, DeliveryType deliveryType)> GetDeliveries(string eventId)
{
using (var db = DbFactory.OpenDbConnection(...))
{
var q = db.From<DeliveryMethod>()
.Join<DeliveryType>((dm, dt) => dm.DeliveryType == dt.DeliveryType)
.Where(dm => dm.EventId == eventId)
.Select<DeliveryMethod, DeliveryType>((dm, dt) =>
new {dm, dt});
return db.Select<(DeliveryMethod deliveryMethod, DeliveryType deliveryType)>(q);
}
}
However, when I run this, I get a NullReferenceException. This seems to be because ConvertToValueTuple in OrmLiteUtils only has converters for basic types like string, int, DateTime, etc. and GetConverter(fieldType) returns null when it's a type of a custom object.
Is there a work around this? Or some way to return a value tuple of more complex, custom objects instead of just basic tuples like (int id, string name, DateTime time)?
P.S. I tried to simplify my problem by simplifying the classes so if I made a mistake there, I apologize for that but I think you can get the basic idea behind my question.
You can only use OrmLite's C# 7 Tuple support by selecting columns, not entire tables, e.g:
.Select<DeliveryMethod, DeliveryType>((dm, dt) =>
new {dm.EventId, dt.DeliveryMethodId});
var results = db.Select<(string, int)>(q);
For Selecting entire tables checkout OrmLite's SelectMulti API, e.g:
var q = db.From<DeliveryMethod>()
.Join<DeliveryType>((dm, dt) => dm.DeliveryType == dt.DeliveryType)
.Where(dm => dm.EventId == eventId);
var results = db.SelectMulti<DeliveryMethod, DeliveryType>();

Automapper error: Expressions mapping from methods not supported yet

Any idea what might cause the error "Expressions mapping from methods not supported yet." when trying to map two objects? I cannot find any reference to this error anywhere.
EDITED---
I have more information. I have a property in my DTO declared as:
public LookupItem RegionType { get; set; }
However, when I invoke the mapping, it generates the error, "Expressions mapping from methods not supported yet.".
However, if I change the string in the property name "Type" to anything else like "Typeo" or "ASDF", the mapping succeeds. In other words, if change the property name to "RegionTypeo". Am I breaking any convention rules here? There seems to be something wrong with including the string "Type" in my property name.
Below is the generated error:
Result Message:
Test method Rep.Tests.PlanServiceTest.GetBuildings threw exception:
System.NotImplementedException: Expressions mapping from methods not supported yet.
Result StackTrace:
at AutoMapper.PropertyMap.ResolveExpression(Type currentType, Expression instanceParameter)
at AutoMapper.QueryableExtensions.Extensions.CreateMemberBindings(IMappingEngine mappingEngine, Type typeIn, TypeMap typeMap, Expression instanceParameter)
at AutoMapper.QueryableExtensions.Extensions.CreateMapExpression(IMappingEngine mappingEngine, Type typeIn, Type typeOut, Expression instanceParameter)
at AutoMapper.QueryableExtensions.Extensions.CreateMapExpression(IMappingEngine mappingEngine, Type typeIn, Type typeOut)
at AutoMapper.QueryableExtensions.Extensions.<>c__DisplayClass12.<CreateMapExpression>b__0(TypePair tp)
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
at AutoMapper.Internal.DictionaryFactoryOverride.ConcurrentDictionaryImpl2.GetOrAdd(TKey key, Func2 valueFactory)
at AutoMapper.QueryableExtensions.Extensions.CreateMapExpression[TSource,TDestination](IMappingEngine mappingEngine)
at AutoMapper.QueryableExtensions.ProjectionExpression1.ToTResult
at Rep.Services.PlanService.GetBuildings() in c:\Dev\REP\Rep\Services\PlanService.cs:line 369
at Rep.Tests.PlanServiceTest.GetBuildings() in c:\Dev\REP\Rep.Tests\PlanServiceTest.cs:line 50
Based on the source code, you can see that the exception is thrown when you try mapping functions on your objects:
public ExpressionResolutionResult ResolveExpression(Type currentType, Expression instanceParameter)
{
Expression currentChild = instanceParameter;
Type currentChildType = currentType;
foreach (var resolver in GetSourceValueResolvers())
{
var getter = resolver as IMemberGetter;
if (getter != null)
{
var memberInfo = getter.MemberInfo;
var propertyInfo = memberInfo as PropertyInfo;
if (propertyInfo != null)
{
currentChild = Expression.Property(currentChild, propertyInfo);
currentChildType = propertyInfo.PropertyType;
}
else
{
throw new NotImplementedException("Expressions mapping from methods not supported yet.");
}
}
else
{
var oldParameter = CustomExpression.Parameters.Single();
var newParameter = instanceParameter;
var converter = new ConversionVisitor(newParameter, oldParameter);
currentChild = converter.Visit(CustomExpression.Body);
currentChildType = currentChild.Type;
}
}
return new ExpressionResolutionResult(currentChild, currentChildType);
}
Based on OP clarification, I cannot reproduce the problem with the following:
public class Class1
{
public string StringType { get; set; }
public Func<Class1> FuncType { get; set; }
public Class1 Class1Type { get; set; }
}
public class Class2
{
public string StringType { get; set; }
public Func<Class1> FuncType { get; set; }
public Class1 Class1Type { get; set; }
}
/* ... */
AutoMapper.Mapper.CreateMap<Class1, Class2>();
var c1 = new Class1() { Class1Type = new Class1(), FuncType = () => new Class1(), StringType = "Class1" };
var c2 = AutoMapper.Mapper.Map<Class1, Class2>(new Class1());

Unable to cast object of type X to Y

I keep getting the exception “Unable to cast object of type X to Y” in some code. I’ve got an interface and two classes that implement it and it keeps throwing this error when casting from one to the other. The two classes and interface are in the same namespace in the same assembly so that’s not the issue. I created an isolated console application to figure this mess out but I can’t get them to cast to one another. I think I’ve forgotten some basic .Net rule here. Anything look off in this code to you?
My isolated app code:
class Program
{
static void Main(string[] args)
{
RecurringPaymentResult r = new RecurringPaymentResult();
r.AddError("test");
ProcessPaymentResult p = null;
p = (ProcessPaymentResult)r; // Doesn't compile. "Cannot convert type RecurringPaymentResult to ProcessPaymentResult"
p = (IPaymentResult)r; // Doesn't compile. "Cannot convert type RecurringPaymentResult to ProcessPaymentResult. An explicit conversion exists (are you missing a cast?)"
p = (ProcessPaymentResult)((IPaymentResult)r); // Compiles but throws: "Unable to cast object of type RecurringPaymentResult to ProcessPaymentResult" during runtime
}
}
My core code:
public interface IPaymentResult
{
IList<string> Errors { get; set; }
bool Success { get; }
void AddError(string error);
}
public partial class RecurringPaymentResult : IPaymentResult
{
public IList<string> Errors { get; set; }
public RecurringPaymentResult()
{
this.Errors = new List<string>();
}
public bool Success
{
get { return (this.Errors.Count == 0); }
}
public void AddError(string error)
{
this.Errors.Add(error);
}
}
public partial class ProcessPaymentResult : IPaymentResult
{
private PaymentStatus _newPaymentStatus = PaymentStatus.Pending;
public IList<string> Errors { get; set; }
public ProcessPaymentResult()
{
this.Errors = new List<string>();
}
public bool Success
{
get { return (this.Errors.Count == 0); }
}
public void AddError(string error)
{
this.Errors.Add(error);
}
// More properties and methods here…
}
One major mistake I see in your code is saying "p =".
You have already decided the type of p and you are assigning various other variables in this type which is wrong.
Below are all possible conversions available :
RecurringPaymentResult r = new RecurringPaymentResult();
r.AddError("test");
ProcessPaymentResult p = null;
var r1 = (IPaymentResult)r; //type of r1 becomes IPaymentResult
var r2 = (IPaymentResult)p; //type of r2 becomes IPaymentResult
var r3 = (RecurringPaymentResult)r1; //type of r3 becomes RecurringPaymentResult
var r4 = (ProcessPaymentResult)r2; //type of r4 becomes ProcessPaymentResult
Logical explaination:
Man (class) has Eye(interface) and Elephant(class) has Eye(interface). Interface provides See method(). Both Man and Deer implements Eye interface by having See method in them.
Now, What you are trying is converting Man object into Elephant object which is not possible as space to store those objects have different requirements.

Why do I need to make Random object static if I call it from the constructors?

class Student
{
private string FirstName { get; set; }
private string LastName { get; set; }
private int age { get; set; }
private int studentID { get; set; }
private static int count = 0;
**static Random randomNumber = new Random(); // works**
**Random randomNumber = new Random(); // doesn't work I get the same studentID number**
public Student()// constructor
{
this.studentID = randomNumber.Next(1000000, 100000000);
count++;
Console.WriteLine("count {0} studentID {1}", count, studentID);
}
public Student(string first, string last, int age)
{
this.studentID = randomNumber.Next(1000000, 100000000);
count++;
Console.WriteLine("count {0} studentID {1}", count, studentID);
this.FirstName = first;
this.LastName = last;
this.age = age;
}
...... continuation
a few get methods
...... continuation
public void PrintData()
{
Console.WriteLine("Name is {0}, Lastname is {1} , Age is {2} , studentID is {3}", FirstName, LastName, age, this.studentID);
}
Why do I keep getting the same number , but if I make the Random object static it generates/assigns new number. Not sure where my logic is faulty.
If you you keep creating new Random objects in the constructor it will reset the seed (If you don't supply arguments the seed is set to the current time (See: MSDN: Random constructor) - so if you create multiple Random objects very near each other (in time) then it will have the same seed). Since Random is not truely random (if you init it from the same seed you'll always get the same sequence back) you will get the same numbers back in the non-static version. The static version is created only once and you keep asking it for the next number in the sequence, hence it appears to give you actual random numbers.

Resources